<commit_before>// This file is a part of the IncludeOS unikernel - www.includeos.org
//
// Copyright 2015-2017 Oslo and Akershus University College of Applied Sciences
// and Alfred Bratterud
//
// 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 <common.cxx>
#include <tar>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
/*
CASE("Reading single entry tar file")
{
  tar::Reader r;
  struct stat st;
  int res = stat("test-single.tar", &st);
  EXPECT(res != -1);
  size_t size = st.st_size;
  int fd = open("test-single.tar", O_RDONLY);
  EXPECT_NOT(fd == -1);
  const uint8_t *mem = (const uint8_t *)mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
  tar::Tar tar = r.read_uncompressed(mem, size);
  EXPECT(tar.num_elements() == 1);
  auto names = tar.element_names();
  EXPECT(names.size() == 1);
  const auto& elements = tar.elements();
  EXPECT(elements.size() == 1);
  const auto& e = elements.at(0);
  EXPECT_NOT(e.is_dir());
  EXPECT(e.typeflag_is_set());
  EXPECT(e.typeflag() == REGTYPE); // regular file
  EXPECT_NOT(e.is_empty());
  EXPECT(e.is_ustar());
  EXPECT_NOT(e.is_tar_gz());
  EXPECT(e.num_content_blocks() > 0);
  EXPECT(e.size() > 0);
  const auto& name = e.name();
  EXPECT(name.find("CMakeLists.txt") != std::string::npos);
  EXPECT_NO_THROW(auto& only_element = tar.element("../CMakeLists.txt"));
  EXPECT_THROWS_AS(auto& missing_element = tar.element("not_there.txt"), tar::Tar_exception);

  // try to decompress a non-tar.gz element
  EXPECT_THROWS_AS(tar::Tar inner_tar = r.decompress(e), tar::Tar_exception);
  close(fd);
}

CASE("Reading multiple entry tar file")
{
  tar::Reader r;
  struct stat st;
  int res = stat("test-multiple.tar", &st);
  EXPECT(res != -1);
  size_t size = st.st_size;
  int fd = open("test-multiple.tar", O_RDONLY);
  EXPECT_NOT(fd == -1);
  const uint8_t *mem = (const uint8_t *)mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
  tar::Tar tar = r.read_uncompressed(mem, size);
  auto names = tar.element_names();
  EXPECT(names.size() > 1);
  const auto& elements = tar.elements();
  EXPECT(elements.size() > 1);
  const auto& e = elements.at(elements.size() - 1);
  EXPECT(e.name().find(".py") != std::string::npos);
  close(fd);
}

CASE("Reading invalid tar file")
{
  tar::Reader r;
  struct stat st;
  int res = stat("test-invalid.tar", &st);
  EXPECT(res != -1);
  size_t size = st.st_size;
  int fd = open("test-invalid.tar", O_RDONLY);
  EXPECT_NOT(fd == -1);
  const uint8_t *mem = (const uint8_t *)mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
  EXPECT_THROWS_AS(tar::Tar tar = r.read_uncompressed(mem, size), tar::Tar_exception);
  close(fd);
}

CASE("Reading corrupt tar.gz file")
{
  tar::Reader r;
  struct stat st;
  int res = stat("test-corrupt.gz", &st);
  EXPECT(res != -1);
  size_t size = st.st_size;
  int fd = open("test-corrupt.gz", O_RDONLY);
  EXPECT_NOT(fd == -1);
  const uint8_t *mem = (const uint8_t *)mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
  EXPECT_THROWS_AS(tar::Tar tar = r.decompress(mem, size), tar::Tar_exception);
  close(fd);
}

CASE("Reading tar.gz inside tar file")
{
  tar::Reader r;
  struct stat st;
  int res = stat("test-tar-gz-inside.tar", &st);
  EXPECT(res != -1);
  size_t size = st.st_size;
  int fd = open("test-tar-gz-inside.tar", O_RDONLY);
  EXPECT_NOT(fd == -1);
  const uint8_t *mem = (const uint8_t *)mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
  tar::Tar tar = r.read_uncompressed(mem, size);
  EXPECT(tar.num_elements() == 1);
  const auto& e = tar.elements().at(0);
  EXPECT(e.is_tar_gz());
  EXPECT(e.name().find(".tar.gz") != std::string::npos);
  tar::Tar inner_tar = r.decompress(e);
  EXPECT(inner_tar.num_elements() == 1);
  const auto& inner_e = inner_tar.elements().at(0);
  EXPECT(inner_e.name().find(".tar.gz") == std::string::npos);
  close(fd);
}
*/
<commit_msg>test: uncomment tar test cases<commit_after>// This file is a part of the IncludeOS unikernel - www.includeos.org
//
// Copyright 2015-2017 Oslo and Akershus University College of Applied Sciences
// and Alfred Bratterud
//
// 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 <common.cxx>
#include <tar>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

CASE("Reading single entry tar file")
{
  tar::Reader r;
  struct stat st;
  int res = stat("test-single.tar", &st);
  EXPECT(res != -1);
  size_t size = st.st_size;
  int fd = open("test-single.tar", O_RDONLY);
  EXPECT_NOT(fd == -1);
  const uint8_t *mem = (const uint8_t *)mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
  tar::Tar tar = r.read_uncompressed(mem, size);
  EXPECT(tar.num_elements() == 1);
  auto names = tar.element_names();
  EXPECT(names.size() == 1);
  const auto& elements = tar.elements();
  EXPECT(elements.size() == 1);
  const auto& e = elements.at(0);
  EXPECT_NOT(e.is_dir());
  EXPECT(e.typeflag_is_set());
  EXPECT(e.typeflag() == REGTYPE); // regular file
  EXPECT_NOT(e.is_empty());
  EXPECT(e.is_ustar());
  EXPECT_NOT(e.is_tar_gz());
  EXPECT(e.num_content_blocks() > 0);
  EXPECT(e.size() > 0);
  const auto& name = e.name();
  EXPECT(name.find("CMakeLists.txt") != std::string::npos);
  EXPECT_NO_THROW(auto& only_element = tar.element("../CMakeLists.txt"));
  EXPECT_THROWS_AS(auto& missing_element = tar.element("not_there.txt"), tar::Tar_exception);

  // try to decompress a non-tar.gz element
  EXPECT_THROWS_AS(tar::Tar inner_tar = r.decompress(e), tar::Tar_exception);
  close(fd);
}

CASE("Reading multiple entry tar file")
{
  tar::Reader r;
  struct stat st;
  int res = stat("test-multiple.tar", &st);
  EXPECT(res != -1);
  size_t size = st.st_size;
  int fd = open("test-multiple.tar", O_RDONLY);
  EXPECT_NOT(fd == -1);
  const uint8_t *mem = (const uint8_t *)mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
  tar::Tar tar = r.read_uncompressed(mem, size);
  auto names = tar.element_names();
  EXPECT(names.size() > 1);
  const auto& elements = tar.elements();
  EXPECT(elements.size() > 1);
  const auto& e = elements.at(elements.size() - 1);
  EXPECT(e.name().find(".py") != std::string::npos);
  close(fd);
}

CASE("Reading invalid tar file")
{
  tar::Reader r;
  struct stat st;
  int res = stat("test-invalid.tar", &st);
  EXPECT(res != -1);
  size_t size = st.st_size;
  int fd = open("test-invalid.tar", O_RDONLY);
  EXPECT_NOT(fd == -1);
  const uint8_t *mem = (const uint8_t *)mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
  EXPECT_THROWS_AS(tar::Tar tar = r.read_uncompressed(mem, size), tar::Tar_exception);
  close(fd);
}

CASE("Reading corrupt tar.gz file")
{
  tar::Reader r;
  struct stat st;
  int res = stat("test-corrupt.gz", &st);
  EXPECT(res != -1);
  size_t size = st.st_size;
  int fd = open("test-corrupt.gz", O_RDONLY);
  EXPECT_NOT(fd == -1);
  const uint8_t *mem = (const uint8_t *)mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
  EXPECT_THROWS_AS(tar::Tar tar = r.decompress(mem, size), tar::Tar_exception);
  close(fd);
}

CASE("Reading tar.gz inside tar file")
{
  tar::Reader r;
  struct stat st;
  int res = stat("test-tar-gz-inside.tar", &st);
  EXPECT(res != -1);
  size_t size = st.st_size;
  int fd = open("test-tar-gz-inside.tar", O_RDONLY);
  EXPECT_NOT(fd == -1);
  const uint8_t *mem = (const uint8_t *)mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
  tar::Tar tar = r.read_uncompressed(mem, size);
  EXPECT(tar.num_elements() == 1);
  const auto& e = tar.elements().at(0);
  EXPECT(e.is_tar_gz());
  EXPECT(e.name().find(".tar.gz") != std::string::npos);
  tar::Tar inner_tar = r.decompress(e);
  EXPECT(inner_tar.num_elements() == 1);
  const auto& inner_e = inner_tar.elements().at(0);
  EXPECT(inner_e.name().find(".tar.gz") == std::string::npos);
  close(fd);
}
<|endoftext|>


<commit_before>#include <Esp.h>
#include <FS.h>
#ifdef ESP32
#include <SPIFFS.h>
#endif
#include "iotsa.h"

// There is an issue with the platformio library dependency finder, and it doesn't find the
// esp8266httpclient library. This is a workaround.
#include "iotsaRequest.h"

// Initialize IotsaSerial (a define) to refer to the normal Serial.
// Will be overridden if the iotsaLogger module is included.
Print *iotsaOverrideSerial = &Serial;

IotsaApplication::IotsaApplication(const char *_title)
: status(NULL),
#ifdef IOTSA_WITH_HTTP_OR_HTTPS
  server(new IotsaWebServer(IOTSA_WEBSERVER_PORT)),
#endif
  firstModule(NULL),
  firstEarlyModule(NULL),
  title(_title),
  haveOTA(false)
{}

void
IotsaApplication::addMod(IotsaBaseMod *mod) {
  mod->nextModule = firstModule;
  firstModule = mod;
}

void
IotsaApplication::addModEarly(IotsaBaseMod *mod) {
  mod->nextModule = firstEarlyModule;
  firstEarlyModule = mod;
}

void
IotsaApplication::setup() {
  IFDEBUG Serial.begin(115200);
  IFDEBUG IotsaSerial.println("Serial opened");
  IFDEBUG IotsaSerial.print("Opening SPIFFS (may take long)...");
  bool ok = SPIFFS.begin();
  IFDEBUG IotsaSerial.println(" done.");
  if (!ok) {
    IFDEBUG IotsaSerial.println("SPIFFS.begin() failed, formatting");

    ok = SPIFFS.format();
    if (!ok) {
      IFDEBUG IotsaSerial.println("SPIFFS.format() failed");
    }
    ok = SPIFFS.begin();
    if (!ok) {
      IFDEBUG IotsaSerial.println("SPIFFS.begin() after format failed");
    }
  } else {
    IFDEBUG IotsaSerial.println("SPIFFS mounted");
  }
  IotsaBaseMod *m;
  for (m=firstEarlyModule; m; m=m->nextModule) {
  	m->setup();
  }
  for (m=firstModule; m; m=m->nextModule) {
  	m->setup();
  }
#ifndef ESP32
  ESP.wdtEnable(WDTO_120MS);
#endif
}

void
IotsaApplication::serverSetup() {
  IotsaBaseMod *m;

  for (m=firstEarlyModule; m; m=m->nextModule) {
  	m->serverSetup();
  }

#ifdef IOTSA_WITH_HTTP_OR_HTTPS
  server->onNotFound(std::bind(&IotsaApplication::webServerNotFoundHandler, this));
#endif
#ifdef IOTSA_WITH_WEB
  server->on("/", std::bind(&IotsaApplication::webServerRootHandler, this));
#endif

  for (m=firstModule; m; m=m->nextModule) {
  	m->serverSetup();
  }
#ifdef IOTSA_WITH_HTTP_OR_HTTPS
  webServerSetup();
#endif
}

void
IotsaApplication::loop() {
  IotsaBaseMod *m;
  for (m=firstEarlyModule; m; m=m->nextModule) {
  	m->loop();
  }
  for (m=firstModule; m; m=m->nextModule) {
  	m->loop();
  }
#ifdef IOTSA_WITH_HTTP_OR_HTTPS
  webServerLoop();
#endif
}

#ifdef IOTSA_WITH_HTTP_OR_HTTPS
void
IotsaApplication::webServerSetup() {
#ifdef IOTSA_WITH_HTTPS
  IFDEBUG IotsaSerial.print("Using https key len=");
  IFDEBUG IotsaSerial.print(iotsaConfig.httpsKeyLength);
  IFDEBUG IotsaSerial.print(", cert len=");
  IFDEBUG IotsaSerial.println(iotsaConfig.httpsCertificateLength);
  server->setServerKeyAndCert_P(
    iotsaConfig.httpsKey,
    iotsaConfig.httpsKeyLength,
    iotsaConfig.httpsCertificate,
    iotsaConfig.httpsCertificateLength
  );
#endif
  server->begin();
#ifdef IOTSA_WITH_HTTPS
  IFDEBUG IotsaSerial.println("HTTPS server started");
#else
  IFDEBUG IotsaSerial.println("HTTP server started");
#endif
}

void
IotsaApplication::webServerLoop() {
  server->handleClient();
}

void
IotsaApplication::webServerNotFoundHandler() {
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server->uri();
  message += "\nMethod: ";
  message += (server->method() == HTTP_GET)?"GET":"POST";
  message += "\nArguments: ";
  message += server->args();
  message += "\n";
  for (uint8_t i=0; i<server->args(); i++){
    message += " " + server->argName(i) + ": " + server->arg(i) + "\n";
  }
  server->send(404, "text/plain", message);
}
#endif // IOTSA_WITH_HTTP_OR_HTTPS

#ifdef IOTSA_WITH_WEB
void
IotsaApplication::webServerRootHandler() {
  String message = "<title>" + title + "</title>
" + title + "
";
  IotsaBaseMod *m;
  for (m=firstModule; m; m=m->nextModule) {
    message += m->info();
  }
  for (m=firstEarlyModule; m; m=m->nextModule) {
    message += m->info();
  }
  message += "";
  server->send(200, "text/html", message);
}

String IotsaBaseMod::info() {
  // Info method that does nothing, usually overridden for IotsaMod modules
  return "";
}

String IotsaMod::htmlEncode(String data) {
  const char *p = data.c_str();
  String rv = "";
  while(p && *p) {
    char escapeChar = *p++;
    switch(escapeChar) {
      case '&': rv += "&"; break;
      case '<': rv += "<"; break;
      case '>': rv += ">"; break;
      case '"': rv += """; break;
      case '\'': rv += "'"; break;
      case '/': rv += "/"; break;
      default: rv += escapeChar; break;
    }
  }
  return rv;
}

//
// Decode percent-escaped string src.
//
void IotsaMod::percentDecode(const String &src, String &dst) {
    const char *arg = src.c_str();
    dst = String();
    while (*arg) {
      char newch;
      if (*arg == '+') newch = ' ';
      else if (*arg == '%') {
        arg++;
        if (*arg == 0) break;
        if (*arg >= '0' && *arg <= '9') newch = (*arg-'0') << 4;
        if (*arg >= 'a' && *arg <= 'f') newch = (*arg-'a'+10) << 4;
        if (*arg >= 'A' && *arg <= 'F') newch = (*arg-'A'+10) << 4;
        arg++;
        if (*arg == 0) break;
        if (*arg >= '0' && *arg <= '9') newch |= (*arg-'0');
        if (*arg >= 'a' && *arg <= 'f') newch |= (*arg-'a'+10);
        if (*arg >= 'A' && *arg <= 'F') newch |= (*arg-'A'+10);
      } else {
        newch = *arg;
      }
      dst += newch;
      arg++;
    }
}
#endif // IOTSA_WITH_WEB

bool IotsaBaseMod::needsAuthentication(const char *object, IotsaApiOperation verb) {
  return auth ? !auth->allows(object, verb) : false;
}

bool IotsaBaseMod::needsAuthentication(const char *right) {
  return auth ? !auth->allows(right) : false;
}

void IotsaBaseMod::serverSetup() {
  // setup method that does nothing, usually overridden for IotsaMod modules
}
<commit_msg>Optional debug<commit_after>#include <Esp.h>
#include <FS.h>
#ifdef ESP32
#include <SPIFFS.h>
#endif
#include "iotsa.h"

// There is an issue with the platformio library dependency finder, and it doesn't find the
// esp8266httpclient library. This is a workaround.
#include "iotsaRequest.h"

// Initialize IotsaSerial (a define) to refer to the normal Serial.
// Will be overridden if the iotsaLogger module is included.
Print *iotsaOverrideSerial = &Serial;

IotsaApplication::IotsaApplication(const char *_title)
: status(NULL),
#ifdef IOTSA_WITH_HTTP_OR_HTTPS
  server(new IotsaWebServer(IOTSA_WEBSERVER_PORT)),
#endif
  firstModule(NULL),
  firstEarlyModule(NULL),
  title(_title),
  haveOTA(false)
{}

void
IotsaApplication::addMod(IotsaBaseMod *mod) {
  mod->nextModule = firstModule;
  firstModule = mod;
}

void
IotsaApplication::addModEarly(IotsaBaseMod *mod) {
  mod->nextModule = firstEarlyModule;
  firstEarlyModule = mod;
}

void
IotsaApplication::setup() {
  IFDEBUG Serial.begin(115200);
  IFDEBUG IotsaSerial.println("Serial opened");
  IFDEBUG IotsaSerial.print("Opening SPIFFS (may take long)...");
  bool ok = SPIFFS.begin();
  IFDEBUG IotsaSerial.println(" done.");
  if (!ok) {
    IFDEBUG IotsaSerial.println("SPIFFS.begin() failed, formatting");

    ok = SPIFFS.format();
    if (!ok) {
      IFDEBUG IotsaSerial.println("SPIFFS.format() failed");
    }
    ok = SPIFFS.begin();
    if (!ok) {
      IFDEBUG IotsaSerial.println("SPIFFS.begin() after format failed");
    }
  } else {
    IFDEBUG IotsaSerial.println("SPIFFS mounted");
  }
  IotsaBaseMod *m;
  for (m=firstEarlyModule; m; m=m->nextModule) {
  	m->setup();
  }
  for (m=firstModule; m; m=m->nextModule) {
  	m->setup();
  }
#ifndef ESP32
  ESP.wdtEnable(WDTO_120MS);
#endif
}

void
IotsaApplication::serverSetup() {
  IotsaBaseMod *m;

  for (m=firstEarlyModule; m; m=m->nextModule) {
  	m->serverSetup();
  }

#ifdef IOTSA_WITH_HTTP_OR_HTTPS
  server->onNotFound(std::bind(&IotsaApplication::webServerNotFoundHandler, this));
#endif
#ifdef IOTSA_WITH_WEB
  server->on("/", std::bind(&IotsaApplication::webServerRootHandler, this));
#endif

  for (m=firstModule; m; m=m->nextModule) {
  	m->serverSetup();
  }
#ifdef IOTSA_WITH_HTTP_OR_HTTPS
  webServerSetup();
#endif
}

void
IotsaApplication::loop() {
  IotsaBaseMod *m;
  for (m=firstEarlyModule; m; m=m->nextModule) {
  	m->loop();
  }
  for (m=firstModule; m; m=m->nextModule) {
  	m->loop();
  }
#ifdef IOTSA_WITH_HTTP_OR_HTTPS
  webServerLoop();
#endif
}

#ifdef IOTSA_WITH_HTTP_OR_HTTPS
void
IotsaApplication::webServerSetup() {
#ifdef IOTSA_WITH_HTTPS
  IFDEBUG IotsaSerial.print("Using https key len=");
  IFDEBUG IotsaSerial.print(iotsaConfig.httpsKeyLength);
  IFDEBUG IotsaSerial.print(", cert len=");
  IFDEBUG IotsaSerial.println(iotsaConfig.httpsCertificateLength);
#if 0
  //xxxjack
  for(int i=0; i<iotsaConfig.httpsCertificateLength; i++) {
    IotsaSerial.print(pgm_read_byte(iotsaConfig.httpsCertificate+i), HEX);
    IotsaSerial.print(' ');
  }
  IotsaSerial.println();
  //xxxjack
#endif
  server->setServerKeyAndCert_P(
    iotsaConfig.httpsKey,
    iotsaConfig.httpsKeyLength,
    iotsaConfig.httpsCertificate,
    iotsaConfig.httpsCertificateLength
  );
#endif
  server->begin();
#ifdef IOTSA_WITH_HTTPS
  IFDEBUG IotsaSerial.println("HTTPS server started");
#else
  IFDEBUG IotsaSerial.println("HTTP server started");
#endif
}

void
IotsaApplication::webServerLoop() {
  server->handleClient();
}

void
IotsaApplication::webServerNotFoundHandler() {
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server->uri();
  message += "\nMethod: ";
  message += (server->method() == HTTP_GET)?"GET":"POST";
  message += "\nArguments: ";
  message += server->args();
  message += "\n";
  for (uint8_t i=0; i<server->args(); i++){
    message += " " + server->argName(i) + ": " + server->arg(i) + "\n";
  }
  server->send(404, "text/plain", message);
}
#endif // IOTSA_WITH_HTTP_OR_HTTPS

#ifdef IOTSA_WITH_WEB
void
IotsaApplication::webServerRootHandler() {
  String message = "<title>" + title + "</title>
" + title + "
";
  IotsaBaseMod *m;
  for (m=firstModule; m; m=m->nextModule) {
    message += m->info();
  }
  for (m=firstEarlyModule; m; m=m->nextModule) {
    message += m->info();
  }
  message += "";
  server->send(200, "text/html", message);
}

String IotsaBaseMod::info() {
  // Info method that does nothing, usually overridden for IotsaMod modules
  return "";
}

String IotsaMod::htmlEncode(String data) {
  const char *p = data.c_str();
  String rv = "";
  while(p && *p) {
    char escapeChar = *p++;
    switch(escapeChar) {
      case '&': rv += "&"; break;
      case '<': rv += "<"; break;
      case '>': rv += ">"; break;
      case '"': rv += """; break;
      case '\'': rv += "'"; break;
      case '/': rv += "/"; break;
      default: rv += escapeChar; break;
    }
  }
  return rv;
}

//
// Decode percent-escaped string src.
//
void IotsaMod::percentDecode(const String &src, String &dst) {
    const char *arg = src.c_str();
    dst = String();
    while (*arg) {
      char newch;
      if (*arg == '+') newch = ' ';
      else if (*arg == '%') {
        arg++;
        if (*arg == 0) break;
        if (*arg >= '0' && *arg <= '9') newch = (*arg-'0') << 4;
        if (*arg >= 'a' && *arg <= 'f') newch = (*arg-'a'+10) << 4;
        if (*arg >= 'A' && *arg <= 'F') newch = (*arg-'A'+10) << 4;
        arg++;
        if (*arg == 0) break;
        if (*arg >= '0' && *arg <= '9') newch |= (*arg-'0');
        if (*arg >= 'a' && *arg <= 'f') newch |= (*arg-'a'+10);
        if (*arg >= 'A' && *arg <= 'F') newch |= (*arg-'A'+10);
      } else {
        newch = *arg;
      }
      dst += newch;
      arg++;
    }
}
#endif // IOTSA_WITH_WEB

bool IotsaBaseMod::needsAuthentication(const char *object, IotsaApiOperation verb) {
  return auth ? !auth->allows(object, verb) : false;
}

bool IotsaBaseMod::needsAuthentication(const char *right) {
  return auth ? !auth->allows(right) : false;
}

void IotsaBaseMod::serverSetup() {
  // setup method that does nothing, usually overridden for IotsaMod modules
}
<|endoftext|>


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

#include "base/idle_timer.h"
#include "base/message_loop.h"
#include "testing/gtest/include/gtest/gtest.h"

using base::Time;
using base::TimeDelta;
using base::IdleTimer;

namespace {

// We Mock the GetLastInputInfo function to return
// the time stored here.
static Time mock_timer_started;

bool MockIdleTimeSource(int32 *milliseconds_interval_since_last_event) {
  TimeDelta delta = Time::Now() - mock_timer_started;
  *milliseconds_interval_since_last_event =
      static_cast<int32>(delta.InMilliseconds());
  return true;
}

// TestIdle task fires after 100ms of idle time.
class TestIdleTask : public IdleTimer {
 public:
  TestIdleTask(bool repeat)
      : IdleTimer(TimeDelta::FromMilliseconds(100), repeat),
        idle_counter_(0) {
        set_idle_time_source(MockIdleTimeSource);
  }

  int get_idle_counter() { return idle_counter_; }

  virtual void OnIdle() {
    idle_counter_++;
  }

 private:
  int idle_counter_;
};

// A task to help us quit the test.
class TestFinishedTask {
 public:
  TestFinishedTask() {}
  void Run() {
    MessageLoop::current()->Quit();
  }
};

// A timer which resets the idle clock.
class ResetIdleTask {
 public:
  ResetIdleTask() {}
  void Run() {
    mock_timer_started = Time::Now();
  }
};

class IdleTimerTest : public testing::Test {
 private:
  // IdleTimer requires a UI message loop on the current thread.
  MessageLoopForUI message_loop_;
};

///////////////////////////////////////////////////////////////////////////////
// NoRepeat tests:
// A non-repeating idle timer will fire once on idle, and
// then will not fire again unless it goes non-idle first.

TEST_F(IdleTimerTest, NoRepeatIdle) {
  // Create an IdleTimer, which should fire once after 100ms.
  // Create a Quit timer which will fire after 1s.
  // Verify that we fired exactly once.

  mock_timer_started = Time::Now();
  TestIdleTask test_task(false);

  TestFinishedTask finish_task;
  base::OneShotTimer<TestFinishedTask> timer;
  timer.Start(TimeDelta::FromSeconds(1), &finish_task, &TestFinishedTask::Run);

  test_task.Start();
  MessageLoop::current()->Run();

  EXPECT_EQ(test_task.get_idle_counter(), 1);
}

TEST_F(IdleTimerTest, NoRepeatFlipIdleOnce) {
  // Create an IdleTimer, which should fire once after 100ms.
  // Create a Quit timer which will fire after 1s.
  // Create a timer to reset once, idle after 500ms.
  // Verify that we fired exactly twice.

  mock_timer_started = Time::Now();
  TestIdleTask test_task(false);

  TestFinishedTask finish_task;
  ResetIdleTask reset_task;

  base::OneShotTimer<TestFinishedTask> t1;
  t1.Start(TimeDelta::FromMilliseconds(1000), &finish_task,
           &TestFinishedTask::Run);

  base::OneShotTimer<ResetIdleTask> t2;
  t2.Start(TimeDelta::FromMilliseconds(500), &reset_task,
           &ResetIdleTask::Run);

  test_task.Start();
  MessageLoop::current()->Run();

  EXPECT_EQ(test_task.get_idle_counter(), 2);
}

TEST_F(IdleTimerTest, NoRepeatNotIdle) {
  // Create an IdleTimer, which should fire once after 100ms.
  // Create a Quit timer which will fire after 1s.
  // Create a timer to reset idle every 50ms.
  // Verify that we never fired.

  mock_timer_started = Time::Now();
  TestIdleTask test_task(false);

  TestFinishedTask finish_task;
  ResetIdleTask reset_task;

  base::OneShotTimer<TestFinishedTask> t;
  t.Start(TimeDelta::FromMilliseconds(1000), &finish_task,
          &TestFinishedTask::Run);

  base::RepeatingTimer<ResetIdleTask> reset_timer;
  reset_timer.Start(TimeDelta::FromMilliseconds(50), &reset_task,
                    &ResetIdleTask::Run);

  test_task.Start();

  MessageLoop::current()->Run();

  reset_timer.Stop();

  EXPECT_EQ(test_task.get_idle_counter(), 0);
}

///////////////////////////////////////////////////////////////////////////////
// Repeat tests:
// A repeating idle timer will fire repeatedly on each interval, as long
// as it has been idle.  So, if the machine remains idle, it will continue
// firing over and over.

TEST_F(IdleTimerTest, Repeat) {
  // Create an IdleTimer, which should fire repeatedly after 100ms.
  // Create a Quit timer which will fire after 1.05s.
  // Verify that we fired 10 times.
  mock_timer_started = Time::Now();
  TestIdleTask test_task(true);

  TestFinishedTask finish_task;

  base::OneShotTimer<TestFinishedTask> t;
  t.Start(TimeDelta::FromMilliseconds(1050), &finish_task,
          &TestFinishedTask::Run);

  test_task.Start();
  MessageLoop::current()->Run();

  // In a perfect world, the idle_counter should be 10.  However,
  // since timers aren't guaranteed to fire perfectly, this can
  // be less.  Just expect more than 5 and no more than 10.
  EXPECT_GT(test_task.get_idle_counter(), 5);
  EXPECT_LE(test_task.get_idle_counter(), 10);
}

TEST_F(IdleTimerTest, RepeatIdleReset) {
  // Create an IdleTimer, which should fire repeatedly after 100ms.
  // Create a Quit timer which will fire after 1s.
  // Create a reset timer, which fires after 550ms
  // Verify that we fired 9 times.
  mock_timer_started = Time::Now();
  TestIdleTask test_task(true);

  ResetIdleTask reset_task;
  TestFinishedTask finish_task;

  base::OneShotTimer<TestFinishedTask> t1;
  t1.Start(TimeDelta::FromMilliseconds(1000), &finish_task,
           &TestFinishedTask::Run);

  base::OneShotTimer<ResetIdleTask> t2;
  t2.Start(TimeDelta::FromMilliseconds(550), &reset_task,
           &ResetIdleTask::Run);

  test_task.Start();
  MessageLoop::current()->Run();

  // In a perfect world, the idle_counter should be 9.  However,
  // since timers aren't guaranteed to fire perfectly, this can
  // be less.  Just expect more than 5 and no more than 9.
  EXPECT_GT(test_task.get_idle_counter(), 5);
  EXPECT_LE(test_task.get_idle_counter(), 9);
}

TEST_F(IdleTimerTest, RepeatNotIdle) {
  // Create an IdleTimer, which should fire repeatedly after 100ms.
  // Create a Quit timer which will fire after 1s.
  // Create a timer to reset idle every 50ms.
  // Verify that we never fired.

  mock_timer_started = Time::Now();
  TestIdleTask test_task(true);

  TestFinishedTask finish_task;
  ResetIdleTask reset_task;

  base::OneShotTimer<TestFinishedTask> t;
  t.Start(TimeDelta::FromMilliseconds(1000), &finish_task,
          &TestFinishedTask::Run);

  base::RepeatingTimer<ResetIdleTask> reset_timer;
  reset_timer.Start(TimeDelta::FromMilliseconds(50), &reset_task,
                    &ResetIdleTask::Run);

  test_task.Start();
  MessageLoop::current()->Run();

  reset_timer.Stop();

  EXPECT_EQ(test_task.get_idle_counter(), 0);
}

}  // namespace
<commit_msg>Disable flaky test IdleTimerTest.RepeatIdleReset Review URL: http://codereview.chromium.org/8632<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/idle_timer.h"
#include "base/message_loop.h"
#include "testing/gtest/include/gtest/gtest.h"

using base::Time;
using base::TimeDelta;
using base::IdleTimer;

namespace {

// We Mock the GetLastInputInfo function to return
// the time stored here.
static Time mock_timer_started;

bool MockIdleTimeSource(int32 *milliseconds_interval_since_last_event) {
  TimeDelta delta = Time::Now() - mock_timer_started;
  *milliseconds_interval_since_last_event =
      static_cast<int32>(delta.InMilliseconds());
  return true;
}

// TestIdle task fires after 100ms of idle time.
class TestIdleTask : public IdleTimer {
 public:
  TestIdleTask(bool repeat)
      : IdleTimer(TimeDelta::FromMilliseconds(100), repeat),
        idle_counter_(0) {
        set_idle_time_source(MockIdleTimeSource);
  }

  int get_idle_counter() { return idle_counter_; }

  virtual void OnIdle() {
    idle_counter_++;
  }

 private:
  int idle_counter_;
};

// A task to help us quit the test.
class TestFinishedTask {
 public:
  TestFinishedTask() {}
  void Run() {
    MessageLoop::current()->Quit();
  }
};

// A timer which resets the idle clock.
class ResetIdleTask {
 public:
  ResetIdleTask() {}
  void Run() {
    mock_timer_started = Time::Now();
  }
};

class IdleTimerTest : public testing::Test {
 private:
  // IdleTimer requires a UI message loop on the current thread.
  MessageLoopForUI message_loop_;
};

///////////////////////////////////////////////////////////////////////////////
// NoRepeat tests:
// A non-repeating idle timer will fire once on idle, and
// then will not fire again unless it goes non-idle first.

TEST_F(IdleTimerTest, NoRepeatIdle) {
  // Create an IdleTimer, which should fire once after 100ms.
  // Create a Quit timer which will fire after 1s.
  // Verify that we fired exactly once.

  mock_timer_started = Time::Now();
  TestIdleTask test_task(false);

  TestFinishedTask finish_task;
  base::OneShotTimer<TestFinishedTask> timer;
  timer.Start(TimeDelta::FromSeconds(1), &finish_task, &TestFinishedTask::Run);

  test_task.Start();
  MessageLoop::current()->Run();

  EXPECT_EQ(test_task.get_idle_counter(), 1);
}

TEST_F(IdleTimerTest, NoRepeatFlipIdleOnce) {
  // Create an IdleTimer, which should fire once after 100ms.
  // Create a Quit timer which will fire after 1s.
  // Create a timer to reset once, idle after 500ms.
  // Verify that we fired exactly twice.

  mock_timer_started = Time::Now();
  TestIdleTask test_task(false);

  TestFinishedTask finish_task;
  ResetIdleTask reset_task;

  base::OneShotTimer<TestFinishedTask> t1;
  t1.Start(TimeDelta::FromMilliseconds(1000), &finish_task,
           &TestFinishedTask::Run);

  base::OneShotTimer<ResetIdleTask> t2;
  t2.Start(TimeDelta::FromMilliseconds(500), &reset_task,
           &ResetIdleTask::Run);

  test_task.Start();
  MessageLoop::current()->Run();

  EXPECT_EQ(test_task.get_idle_counter(), 2);
}

TEST_F(IdleTimerTest, NoRepeatNotIdle) {
  // Create an IdleTimer, which should fire once after 100ms.
  // Create a Quit timer which will fire after 1s.
  // Create a timer to reset idle every 50ms.
  // Verify that we never fired.

  mock_timer_started = Time::Now();
  TestIdleTask test_task(false);

  TestFinishedTask finish_task;
  ResetIdleTask reset_task;

  base::OneShotTimer<TestFinishedTask> t;
  t.Start(TimeDelta::FromMilliseconds(1000), &finish_task,
          &TestFinishedTask::Run);

  base::RepeatingTimer<ResetIdleTask> reset_timer;
  reset_timer.Start(TimeDelta::FromMilliseconds(50), &reset_task,
                    &ResetIdleTask::Run);

  test_task.Start();

  MessageLoop::current()->Run();

  reset_timer.Stop();

  EXPECT_EQ(test_task.get_idle_counter(), 0);
}

///////////////////////////////////////////////////////////////////////////////
// Repeat tests:
// A repeating idle timer will fire repeatedly on each interval, as long
// as it has been idle.  So, if the machine remains idle, it will continue
// firing over and over.

TEST_F(IdleTimerTest, Repeat) {
  // Create an IdleTimer, which should fire repeatedly after 100ms.
  // Create a Quit timer which will fire after 1.05s.
  // Verify that we fired 10 times.
  mock_timer_started = Time::Now();
  TestIdleTask test_task(true);

  TestFinishedTask finish_task;

  base::OneShotTimer<TestFinishedTask> t;
  t.Start(TimeDelta::FromMilliseconds(1050), &finish_task,
          &TestFinishedTask::Run);

  test_task.Start();
  MessageLoop::current()->Run();

  // In a perfect world, the idle_counter should be 10.  However,
  // since timers aren't guaranteed to fire perfectly, this can
  // be less.  Just expect more than 5 and no more than 10.
  EXPECT_GT(test_task.get_idle_counter(), 5);
  EXPECT_LE(test_task.get_idle_counter(), 10);
}

// TODO(darin):  http://code.google.com/p/chromium/issues/detail?id=3780
TEST_F(IdleTimerTest, DISABLED_RepeatIdleReset) {
  // Create an IdleTimer, which should fire repeatedly after 100ms.
  // Create a Quit timer which will fire after 1s.
  // Create a reset timer, which fires after 550ms
  // Verify that we fired 9 times.
  mock_timer_started = Time::Now();
  TestIdleTask test_task(true);

  ResetIdleTask reset_task;
  TestFinishedTask finish_task;

  base::OneShotTimer<TestFinishedTask> t1;
  t1.Start(TimeDelta::FromMilliseconds(1000), &finish_task,
           &TestFinishedTask::Run);

  base::OneShotTimer<ResetIdleTask> t2;
  t2.Start(TimeDelta::FromMilliseconds(550), &reset_task,
           &ResetIdleTask::Run);

  test_task.Start();
  MessageLoop::current()->Run();

  // In a perfect world, the idle_counter should be 9.  However,
  // since timers aren't guaranteed to fire perfectly, this can
  // be less.  Just expect more than 5 and no more than 9.
  EXPECT_GT(test_task.get_idle_counter(), 5);
  EXPECT_LE(test_task.get_idle_counter(), 9);
}

TEST_F(IdleTimerTest, RepeatNotIdle) {
  // Create an IdleTimer, which should fire repeatedly after 100ms.
  // Create a Quit timer which will fire after 1s.
  // Create a timer to reset idle every 50ms.
  // Verify that we never fired.

  mock_timer_started = Time::Now();
  TestIdleTask test_task(true);

  TestFinishedTask finish_task;
  ResetIdleTask reset_task;

  base::OneShotTimer<TestFinishedTask> t;
  t.Start(TimeDelta::FromMilliseconds(1000), &finish_task,
          &TestFinishedTask::Run);

  base::RepeatingTimer<ResetIdleTask> reset_timer;
  reset_timer.Start(TimeDelta::FromMilliseconds(50), &reset_task,
                    &ResetIdleTask::Run);

  test_task.Start();
  MessageLoop::current()->Run();

  reset_timer.Stop();

  EXPECT_EQ(test_task.get_idle_counter(), 0);
}

}  // namespace
<|endoftext|>


<commit_before>/* Copyright 2014 Jonas Platte
 *
 * This file is part of Cyvasse Online.
 *
 * Cyvasse Online 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.
 *
 * Cyvasse Online is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License along with this program.
 * If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _CYVMATH_HEXAGON_HPP_
#define _CYVMATH_HEXAGON_HPP_

#include <set>
#include <string>
#include <stdexcept>
#include <cmath>

namespace cyvmath
{
	/* This should rather be a namespace, but namespaces cannot have template parameters.
	   hexagon<6>::Coordinate is way better understandable than hexagon::Coordinate<6>
	   so this is implemented as a template class with a deleted constructor.
	   The template parameter l stands for the edge length of the hexagon.
	 */
	template <int l>
	class hexagon
	{
		/* The smallest possible hexagon has 7 tiles and an edge lenth of 2
		   The largest possible hexagon in which the highest x and y can be represented
		   using a signed 8 bit integer has an edge length of 64 (=> width and height 127)
		 */
		static_assert(l >= 2,  "The minimum size of the hexagon edge length is 2.");
		static_assert(l <= 64, "The maximum size of the hexagon edge length is 64.");

		private:
			hexagon() = delete;

		public:
			/// A coordinate on the hexboard (see mockup/hexboard-coordinates-internal.svg)
			class Coordinate
			{
				private:
					int8_t _x;
					int8_t _y;

					int8_t z()
					{
						return -(_x + _y);
					}

					void throwInvalid()
					{
						throw std::invalid_argument("Coordinate not valid: "
						                            "{" + std::to_string(_x) +
						                            ", " + std::to_string(_y) + "}");
					}

				public:
					Coordinate(int8_t x, int8_t y)
						: _x(x)
						, _y(y)
					{
						if(!isValid())
							throwInvalid();
					}

					bool isValid()
					{
						return (_x + _y) >= (l - 1) &&
						       (_x + _y) <= (l - 1) * 3;
					}

					operator bool()
					{
						return isValid();
					}

					/** Get the distance to another coordinate in form of the amount
					    of single moves to adjacent tiles required to move there
					 */
					int8_t getDistance(Coordinate other)
					{
						// I have no idea how or why this works, but it does.
						// Taken from http://keekerdc.com/2011/03/hexagon-grids-coordinate-systems-and-distance-calculations/
						return *(std::set<int>({abs(_x - other._x), abs(_y - other._y), abs(z() - other.z())}).rbegin());
					}

					/// Check if the given coordinate is reachable in one orthogonal move
					bool isOrthogonal(Coordinate other)
					{
						return _x == other._x || _y == other._y || z() == other.z();
					}

					/** Like getDistance(), but return -1 if the given coordinate
					    is not reachable in one orthogonal move

					    An orthogonal move is a straight move in one of the six
					    directions of the neighboring hex-tiles.
					 */
					int8_t getDistanceOrthogonal(Coordinate other)
					{
						if(!isOrthogonal(other))
							return -1;

						return getDistance(other);
					}

					/// Check if the given coordinate is reachable in one diagonal move
					bool isDiagonal(Coordinate other)
					{
						int8_t dX = abs(_x - other._x);
						int8_t dY = abs(_y - other._y);
						int8_t dZ = abs(z() - other.z());

						return (dX == dY && dX == dZ / 2) ||
						       (dX == dZ && dX == dY / 2) ||
						       (dY == dZ && dY == dX / 2);
					}

					/** Get the diagonal distance to a coordinate, or -1 if
					    the coordinate isn't reachable in one diagonal move

					    A diagonal move is a straight move in one of the six
					    directions of the lines between the neighboring hex-tiles.
					 */
					int8_t getDistanceDiagonal(Coordinate other)
					{
						is(!isDiagonal(other))
							return -1;

						return getDistance(other) / 2;
					}

					/** Get the distance to another coordinate with the same distance to
					    the center Coordinate along the line of these coordinates, or -1
					    if the coordinate isn't reachable while only moving on that line.

					    All possible movement targets here are along a hexagonal line.
					    The direct distance from this coordinate to another one on that
					    line may be different than the distance along the line.
					 */
					// There certainly is no other ruleset than MikeL's that uses this
					// but there probably is no better place to put this code either.
					int8_t getDistanceHexagonalLine(Coordinate other, Coordinate center)
					{
						// Check if the given coordinate has the same distance to center as this
						if(getDistance(center) != other.getDistance(center))
							return -1;

						// TODO
					}

					/// Check if the given coordinate is a direct neighbor
					bool isAdjacent(Coordinate other)
					{
						return getDistance() == 1;
					}

					void set(int8_t x, int8_t y)
					{
						_x = x;
						_y = y;

						if(!isValid())
							throwInvalid();
					}

					Coordinate& operator=(Coordinate other)
					{
						set(other._x, other._y);
						return *this;
					}

					/// Create a Coordinate object from a coordinate in the public notation
					/// (see mockup/hexboard-coordinates-public.svg)
					static Coordinate fromStr(const std::string& str)
					{
						// This function will currently fail on hexagons with l > 13 because there
						// would be multiple letters for the y coordinate in the public notation.
						// TODO: Decide on whether to set a maximum l of 13 or to alter this function

						if(str.length() < 2)
							throw std::invalid_argument("Coordinate not valid");

						/* x: When using char's for calculations, their ASCII values are used,
						      and as the uppercase letters are consecutive in the ASCII table,
						      this will result in 0 for 'A', 1 for 'B' and so on.
						      We don't accept lowercase letters.
						   y: Public coordinate notation starts with 1, we start with 0
						 */
						return {str.at(0) - 'A', std::stoi(str.substr(1)) - 1};
					}
			};
	};
}

#endif // _CYVMATH_HEXAGON_HPP_
<commit_msg>Added get-Methods for x and y in Coordinate, hash function for unordered STL containers<commit_after>/* Copyright 2014 Jonas Platte
 *
 * This file is part of Cyvasse Online.
 *
 * Cyvasse Online 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.
 *
 * Cyvasse Online is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License along with this program.
 * If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _CYVMATH_HEXAGON_HPP_
#define _CYVMATH_HEXAGON_HPP_

#include <functional>
#include <set>
#include <string>
#include <stdexcept>
#include <cmath>

namespace cyvmath
{
	/* This should rather be a namespace, but namespaces cannot have template parameters.
	   hexagon<6>::Coordinate is way better understandable than hexagon::Coordinate<6>
	   so this is implemented as a template class with a deleted constructor.
	   The template parameter l stands for the edge length of the hexagon.
	 */
	template <int l>
	class hexagon
	{
		/* The smallest possible hexagon has 7 tiles and an edge lenth of 2
		   The largest possible hexagon in which the highest x and y can be represented
		   using a signed 8 bit integer has an edge length of 64 (=> width and height 127)
		 */
		static_assert(l >= 2,  "The minimum size of the hexagon edge length is 2.");
		static_assert(l <= 64, "The maximum size of the hexagon edge length is 64.");

		private:
			hexagon() = delete;

		public:
			/// A coordinate on the hexboard (see mockup/hexboard-coordinates-internal.svg)
			class Coordinate
			{
				private:
					int8_t _x;
					int8_t _y;

					int8_t z()
					{
						return -(_x + _y);
					}

					void throwInvalid()
					{
						throw std::invalid_argument("Coordinate not valid: "
						                            "{" + std::to_string(_x) +
						                            ", " + std::to_string(_y) + "}");
					}

				public:
					Coordinate(int8_t x, int8_t y)
						: _x(x)
						, _y(y)
					{
						if(!isValid())
							throwInvalid();
					}

					int8_t x()
					{
						return _x;
					}

					int8_t y()
					{
						return _y;
					}

					bool isValid()
					{
						return (_x + _y) >= (l - 1) &&
						       (_x + _y) <= (l - 1) * 3;
					}

					operator bool()
					{
						return isValid();
					}

					/** Get the distance to another coordinate in form of the amount
					    of single moves to adjacent tiles required to move there
					 */
					int8_t getDistance(Coordinate other)
					{
						// I have no idea how or why this works, but it does.
						// Taken from http://keekerdc.com/2011/03/hexagon-grids-coordinate-systems-and-distance-calculations/
						return *(std::set<int>({abs(_x - other._x), abs(_y - other._y), abs(z() - other.z())}).rbegin());
					}

					/// Check if the given coordinate is reachable in one orthogonal move
					bool isOrthogonal(Coordinate other)
					{
						return _x == other._x || _y == other._y || z() == other.z();
					}

					/** Like getDistance(), but return -1 if the given coordinate
					    is not reachable in one orthogonal move

					    An orthogonal move is a straight move in one of the six
					    directions of the neighboring hex-tiles.
					 */
					int8_t getDistanceOrthogonal(Coordinate other)
					{
						if(!isOrthogonal(other))
							return -1;

						return getDistance(other);
					}

					/// Check if the given coordinate is reachable in one diagonal move
					bool isDiagonal(Coordinate other)
					{
						int8_t dX = abs(_x - other._x);
						int8_t dY = abs(_y - other._y);
						int8_t dZ = abs(z() - other.z());

						return (dX == dY && dX == dZ / 2) ||
						       (dX == dZ && dX == dY / 2) ||
						       (dY == dZ && dY == dX / 2);
					}

					/** Get the diagonal distance to a coordinate, or -1 if
					    the coordinate isn't reachable in one diagonal move

					    A diagonal move is a straight move in one of the six
					    directions of the lines between the neighboring hex-tiles.
					 */
					int8_t getDistanceDiagonal(Coordinate other)
					{
						if(!isDiagonal(other))
							return -1;

						return getDistance(other) / 2;
					}

					/** Get the distance to another coordinate with the same distance to
					    the center Coordinate along the line of these coordinates, or -1
					    if the coordinate isn't reachable while only moving on that line.

					    All possible movement targets here are along a hexagonal line.
					    The direct distance from this coordinate to another one on that
					    line may be different than the distance along the line.
					 */
					// There certainly is no other ruleset than MikeL's that uses this
					// but there probably is no better place to put this code either.
					int8_t getDistanceHexagonalLine(Coordinate other, Coordinate center)
					{
						// Check if the given coordinate has the same distance to center as this
						if(getDistance(center) != other.getDistance(center))
							return -1;

						// TODO
					}

					/// Check if the given coordinate is a direct neighbor
					bool isAdjacent(Coordinate other)
					{
						return getDistance() == 1;
					}

					void set(int8_t x, int8_t y)
					{
						_x = x;
						_y = y;

						if(!isValid())
							throwInvalid();
					}

					Coordinate& operator=(Coordinate other)
					{
						set(other._x, other._y);
						return *this;
					}

					/// Create a Coordinate object from a coordinate in the public notation
					/// (see mockup/hexboard-coordinates-public.svg)
					static Coordinate fromStr(const std::string& str)
					{
						// This function will currently fail on hexagons with l > 13 because there
						// would be multiple letters for the y coordinate in the public notation.
						// TODO: Decide on whether to set a maximum l of 13 or to alter this function

						if(str.length() < 2)
							throw std::invalid_argument("Coordinate not valid");

						/* x: When using char's for calculations, their ASCII values are used,
						      and as the uppercase letters are consecutive in the ASCII table,
						      this will result in 0 for 'A', 1 for 'B' and so on.
						      We don't accept lowercase letters.
						   y: Public coordinate notation starts with 1, we start with 0
						 */
						return {str.at(0) - 'A', std::stoi(str.substr(1)) - 1};
					}
			};

			struct CoordinateHash
			{
				size_t operator()(const Coordinate& c) const
				{
					return std::hash<int>() ((c.x() << 8) | c.y());
				}
			};
	};
}

#endif // _CYVMATH_HEXAGON_HPP_
<|endoftext|>


<commit_before>#pragma once

#include <array>
#include <algorithm>
#include <type_traits>

#include "type_traits.hpp"
#include "detail/tmp.hpp"

namespace sor {

	/* Implementation details.
	*/
	namespace detail {

		template<std::size_t... Dims, typename... Args>
		std::size_t flatten_indexes(Args... args) {

			std::initializer_list<std::size_t> indexes = { static_cast<std::size_t>(args)... };
			std::initializer_list<std::size_t> dimensions = { Dims... };

			std::size_t index = 0;
			auto last_index = indexes.end() - 1;
			for (
					auto i = indexes.begin(), d = dimensions.end() - 1;
					i < last_index && d >= dimensions.begin();
					i++, d--
				) {
				index += *i;
				index *= *d;
			}
			index += *last_index;

			return index;

		}

	}

	/* The tensor class.
	*/
	template<typename Type, std::size_t... Dims>
	struct tensor_facade {

	private:

		using tensor_facade_type = tensor_facade<Type, Dims...>;
		using container_type = std::array<Type, detail::multiply<Dims...>::value>;

		container_type array;

	public:

		/* Type definitions
		*/
		using value_type = Type;
		using reference = Type&;
		using const_reference = Type const&;

		/* Friend related tensor facade classes
		*/
		template<typename OtherType, std::size_t... OtherDims>
		friend struct tensor_facade;

		/* Regular default, copy and move constructors work as you would expect.
		*/
		tensor_facade() = default;
		tensor_facade(tensor_facade const&) = default;
		tensor_facade(tensor_facade&&) = default;

		/* Templated copy constructor
		*/
		template<typename OtherType>
		tensor_facade(tensor_facade<OtherType, Dims...> const& other) {
			std::copy(other.array.begin(), other.array.end(), array.begin());
		}

		/* Constructor that initializes the array as if you were to initialize an array of multiple
		 * dimensions (left to right, top to bottom, front to back, etc...).
		*/
		explicit tensor_facade(std::initializer_list<Type> const& list) {
			std::copy(list.begin(), list.end(), array.begin());
		}

		/* Copy and move assignment work as you would normally expect.
		*/
		tensor_facade& operator=(tensor_facade const&) = default;
		tensor_facade& operator=(tensor_facade&&) = default;

		/* Iterators
		*/
		using iterator = typename container_type::iterator;
		using const_iterator = typename container_type::const_iterator;

		constexpr iterator begin() { return array.begin(); }
		constexpr const_iterator begin() const { return array.begin(); }
		constexpr const_iterator cbegin() const {
			return const_cast<tensor_facade_type const&>(*this).begin();
		}

		constexpr iterator end() { return array.end(); }
		constexpr const_iterator end() const { return array.end(); }
		constexpr const_iterator cend() const {
			return const_cast<tensor_facade_type const&>(*this).end();
		}

		/* Swap function
		*/
		void swap(tensor_facade& rhs) {
			array.swap(rhs.array);
		}

		/* Element access operator. It access the member based on the given indexes.
		 * Example:
		 * 		sor::tensor<int, 3, 4> matrix({
		 * 			0, 1, 2, 3,
		 * 			4, 5, 6, 7,
		 * 			8, 9, 10, 11
		 * 		});
		 *		std::cout << matrix(2, 2); // = 6
		*/
		template<typename... Args>
		Type& operator()(Args... args) {
			static_assert(
				sizeof...(Args) == sizeof...(Dims),
				"Number of indeces and order don't match"
			);
			auto index = detail::flatten_indexes<Dims...>(args...);
			return array[index];
		}

		template<typename... Args>
		Type const& operator()(Args... args) const {
			static_assert(
				sizeof...(Args) == sizeof...(Dims),
				"Number of indeces and order don't match"
			);
			auto index = detail::flatten_indexes<Dims...>(args...);
			return array[index];
		}

		/* Size related member functions
		*/
		using difference_type = typename container_type::difference_type;
		using size_type = typename container_type::size_type;

		constexpr size_type size() const {
			return detail::multiply<Dims...>::value;
		}

		constexpr size_type max_size() const {
			return this->size();
		}

		constexpr bool empty() const {
			return size() == 0;
		}

	};

}<commit_msg>Removed static asserts and put enable_ifs instead on operator() for tensors<commit_after>#pragma once

#include <array>
#include <algorithm>
#include <type_traits>

#include "type_traits.hpp"
#include "detail/tmp.hpp"

namespace sor {

	/* Implementation details.
	*/
	namespace detail {

		template<std::size_t... Dims, typename... Args>
		std::size_t flatten_indexes(Args... args) {

			std::initializer_list<std::size_t> indexes = { static_cast<std::size_t>(args)... };
			std::initializer_list<std::size_t> dimensions = { Dims... };

			std::size_t index = 0;
			auto last_index = indexes.end() - 1;
			for (
					auto i = indexes.begin(), d = dimensions.end() - 1;
					i < last_index && d >= dimensions.begin();
					i++, d--
				) {
				index += *i;
				index *= *d;
			}
			index += *last_index;

			return index;

		}

	}

	/* The tensor class.
	*/
	template<typename Type, std::size_t... Dims>
	struct tensor_facade {

	private:

		using tensor_facade_type = tensor_facade<Type, Dims...>;
		using container_type = std::array<Type, detail::multiply<Dims...>::value>;

		container_type array;

	public:

		/* Type definitions
		*/
		using value_type = Type;
		using reference = Type&;
		using const_reference = Type const&;

		/* Friend related tensor facade classes
		*/
		template<typename OtherType, std::size_t... OtherDims>
		friend struct tensor_facade;

		/* Regular default, copy and move constructors work as you would expect.
		*/
		tensor_facade() = default;
		tensor_facade(tensor_facade const&) = default;
		tensor_facade(tensor_facade&&) = default;

		/* Templated copy constructor
		*/
		template<typename OtherType>
		tensor_facade(tensor_facade<OtherType, Dims...> const& other) {
			std::copy(other.array.begin(), other.array.end(), array.begin());
		}

		/* Constructor that initializes the array as if you were to initialize an array of multiple
		 * dimensions (left to right, top to bottom, front to back, etc...).
		*/
		explicit tensor_facade(std::initializer_list<Type> const& list) {
			std::copy(list.begin(), list.end(), array.begin());
		}

		/* Copy and move assignment work as you would normally expect.
		*/
		tensor_facade& operator=(tensor_facade const&) = default;
		tensor_facade& operator=(tensor_facade&&) = default;

		/* Iterators
		*/
		using iterator = typename container_type::iterator;
		using const_iterator = typename container_type::const_iterator;

		constexpr iterator begin() { return array.begin(); }
		constexpr const_iterator begin() const { return array.begin(); }
		constexpr const_iterator cbegin() const {
			return const_cast<tensor_facade_type const&>(*this).begin();
		}

		constexpr iterator end() { return array.end(); }
		constexpr const_iterator end() const { return array.end(); }
		constexpr const_iterator cend() const {
			return const_cast<tensor_facade_type const&>(*this).end();
		}

		/* Swap function
		*/
		void swap(tensor_facade& rhs) {
			array.swap(rhs.array);
		}

		/* Element access operator. It access the member based on the given indexes.
		 * Example:
		 * 		sor::tensor<int, 3, 4> matrix({
		 * 			0, 1, 2, 3,
		 * 			4, 5, 6, 7,
		 * 			8, 9, 10, 11
		 * 		});
		 *		std::cout << matrix(2, 2); // = 6
		*/
		template<typename... Args,
			typename std::enable_if<sizeof...(Args) == sizeof...(Dims), int>::type = 0>
		Type& operator()(Args... args) {
			auto index = detail::flatten_indexes<Dims...>(args...);
			return array[index];
		}

		template<typename... Args,
			typename std::enable_if<sizeof...(Args) == sizeof...(Dims), int>::type = 0>
		Type const& operator()(Args... args) const {
			auto index = detail::flatten_indexes<Dims...>(args...);
			return array[index];
		}

		/* Size related member functions
		*/
		using difference_type = typename container_type::difference_type;
		using size_type = typename container_type::size_type;

		constexpr size_type size() const {
			return detail::multiply<Dims...>::value;
		}

		constexpr size_type max_size() const {
			return this->size();
		}

		constexpr bool empty() const {
			return size() == 0;
		}

	};

}<|endoftext|>


<commit_before>#include <iostream>
#include <gtest/gtest.h>
#include "../pipeline.h"
#include "../channel.h"
#include "../scheduler.h"
#include <thread>
#include <asyncply/run.h>

class ChannelTest : testing::Test { };

using cmd = cu::pipeline<int>;

cmd::link generator()
{
	return [](cmd::in&, cmd::out& yield)
	{
		for (auto& s : {100, 200, 300})
		{
			std::cout << "I am generator and push " << s << std::endl;
			yield(s);
		}
	};
}

cmd::link link1()
{
	return [](cmd::in& source, cmd::out& yield)
	{
		for (auto& s : source)
		{
			std::cout << "I am link1 and push " << s << std::endl;
			yield(s);
		}
	};
}

cmd::link link2()
{
	return [](cmd::in& source, cmd::out& yield)
	{
		for (auto& s : source)
		{
			std::cout << "I am link2 and push " << s << std::endl;
			yield(s);
		}
	};
}

cmd::link link3()
{
	return [](cmd::in& source, cmd::out& yield)
	{
		for (auto& s : source)
		{
			std::cout << "I am link3 and push " << s << std::endl;
			yield(s);
		}
	};
}

TEST(ChannelTest, pipeline)
{
	cmd(generator(), link1(), link2(), link3());
}

TEST(ChannelTest, goroutines_consumer)
{
	cu::scheduler sch;
	cu::channel<int> go(sch, 10);
	sch.spawn("producer", [&sch, &go](auto& yield) {
		// send
		for(int i=0; i<100; ++i)
		{
			std::cout << "sending: " << i << std::endl;
			go(yield, i);
		}
		go.close(yield);
	});
	sch.spawn("consumer", [&sch, &go](auto& yield) {
		// recv
		for(;;)
		{
			auto data = go.get(yield);
			if(!data)
			{
				std::cout << "channel closed" << std::endl;
				break;
			}
			else
			{
				std::cout << "recving: " << *data << std::endl;
			}
		}
	});
	sch.run_until_complete();
}

TEST(CoroTest, TestScheduler)
{
	cu::scheduler sch;
	cu::semaphore person1(sch);
	cu::semaphore person2(sch);
	cu::semaphore other(sch, 2);
	// person2
	sch.spawn("person2", [&](auto& yield) {
		std::cout << "Hola person1" << std::endl;
		person2.notify(yield);
		//
		person1.wait(yield);
		std::cout << "que tal ?" << std::endl;
		person2.notify(yield);
		//
		person1.wait(yield);
		std::cout << "me alegro" << std::endl;
		person2.notify(yield);
		//
		other.notify(yield);
	});
	// person1
	sch.spawn("person1", [&](auto& yield) {
		//
		person2.wait(yield);
		std::cout << "Hola person2" << std::endl;
		person1.notify(yield);
		//
		person2.wait(yield);
		std::cout << "bien!" << std::endl;
		person1.notify(yield);
		//
		person2.wait(yield);
		std::cout << "y yo ^^" << std::endl;
		//
		other.notify(yield);
	});
	// other
	sch.spawn("other", [&](auto& yield) {
		//
		other.wait(yield);
		std::cout << "parar!!! tengo algo importante" << std::endl;
	});
	sch.run_until_complete();
}
<commit_msg>Update test_channel.cpp<commit_after>#include <iostream>
#include <gtest/gtest.h>
#include "../pipeline.h"
#include "../channel.h"
#include "../scheduler.h"
#include <thread>
#include <asyncply/run.h>

class ChannelTest : testing::Test { };

using cmd = cu::pipeline<int>;

cmd::link generator()
{
	return [](cmd::in&, cmd::out& yield)
	{
		for (auto& s : {100, 200, 300})
		{
			std::cout << "I am generator and push " << s << std::endl;
			yield(s);
		}
	};
}

cmd::link link1()
{
	return [](cmd::in& source, cmd::out& yield)
	{
		for (auto& s : source)
		{
			std::cout << "I am link1 and push " << s << std::endl;
			yield(s);
		}
	};
}

cmd::link link2()
{
	return [](cmd::in& source, cmd::out& yield)
	{
		for (auto& s : source)
		{
			std::cout << "I am link2 and push " << s << std::endl;
			yield(s);
		}
	};
}

cmd::link link3()
{
	return [](cmd::in& source, cmd::out& yield)
	{
		for (auto& s : source)
		{
			std::cout << "I am link3 and push " << s << std::endl;
			yield(s);
		}
	};
}

TEST(ChannelTest, pipeline)
{
	cmd(generator(), link1(), link2(), link3());
}

TEST(ChannelTest, goroutines_consumer)
{
	cu::scheduler sch;
	cu::channel<int> go(sch, 10);
	sch.spawn("producer", [&sch, &go](auto& yield) {
		// send
		for(int i=0; i<200; ++i)
		{
			std::cout << "sending: " << i << std::endl;
			go(yield, i);
		}
		go.close(yield);
	});
	sch.spawn("consumer", [&sch, &go](auto& yield) {
		// recv
		for(;;)
		{
			auto data = go.get(yield);
			if(!data)
			{
				std::cout << "channel closed" << std::endl;
				break;
			}
			else
			{
				std::cout << "recving: " << *data << std::endl;
			}
		}
	});
	sch.run_until_complete();
}

TEST(CoroTest, TestScheduler)
{
	cu::scheduler sch;
	cu::semaphore person1(sch);
	cu::semaphore person2(sch);
	cu::semaphore other(sch, 2);
	// person2
	sch.spawn("person2", [&](auto& yield) {
		std::cout << "Hola person1" << std::endl;
		person2.notify(yield);
		//
		person1.wait(yield);
		std::cout << "que tal ?" << std::endl;
		person2.notify(yield);
		//
		person1.wait(yield);
		std::cout << "me alegro" << std::endl;
		person2.notify(yield);
		//
		other.notify(yield);
	});
	// person1
	sch.spawn("person1", [&](auto& yield) {
		//
		person2.wait(yield);
		std::cout << "Hola person2" << std::endl;
		person1.notify(yield);
		//
		person2.wait(yield);
		std::cout << "bien!" << std::endl;
		person1.notify(yield);
		//
		person2.wait(yield);
		std::cout << "y yo ^^" << std::endl;
		//
		other.notify(yield);
	});
	// other
	sch.spawn("other", [&](auto& yield) {
		//
		other.wait(yield);
		std::cout << "parar!!! tengo algo importante" << std::endl;
	});
	sch.run_until_complete();
}
<|endoftext|>


<commit_before>#include "../../Flare.h"
#include "FlareAIBehavior.h"

#include "../FlareGame.h"
#include "../FlareCompany.h"
#include "../FlareScenarioTools.h"
#include "../../Spacecrafts/FlareSimulatedSpacecraft.h"

/*----------------------------------------------------
	Public API
----------------------------------------------------*/

UFlareAIBehavior::UFlareAIBehavior(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
}

void UFlareAIBehavior::Load(UFlareCompany* ParentCompany)
{
	if(!Company)
	{
		Company = ParentCompany;
		check(Company);
		Game = Company->GetGame();
		ST = Game->GetScenarioTools();
		check(ST);

		GenerateAffilities();
	}
}

void UFlareAIBehavior::Simulate()
{
	if(Company == ST->Pirates)
	{
		SimulatePirateBehavior();
	}
	else
	{
		SimulateGeneralBehavior();
	}

}

void UFlareAIBehavior::SimulateGeneralBehavior()
{
	// Update trade routes
	int32 IdleCargoCapacity = Company->GetAI()->UpdateTrading();

	// Create or upgrade stations
	Company->GetAI()->UpdateStationConstruction(IdleCargoCapacity);

	// Buy ships
	Company->GetAI()->UpdateShipAcquisition(IdleCargoCapacity);

	Company->GetAI()->UpdateMilitaryMovement(true);
}

void UFlareAIBehavior::UpdateDiplomacy()
{

	// Simulate company attitude towards others
	for (int32 CompanyIndex = 0; CompanyIndex < Game->GetGameWorld()->GetCompanies().Num(); CompanyIndex++)
	{
		UFlareCompany* OtherCompany = Game->GetGameWorld()->GetCompanies()[CompanyIndex];

		if (OtherCompany == Company)
		{
			continue;
		}

		if(Company == ST->Pirates)
		{
			Company->SetHostilityTo(OtherCompany, true);
		}
		else
		{
			if (Company->GetHostility(OtherCompany) == EFlareHostility::Hostile && Company->GetReputation(OtherCompany) > -100)
			{
				Company->SetHostilityTo(OtherCompany, false);
			}
			else if (Company->GetHostility(OtherCompany) != EFlareHostility::Hostile && Company->GetReputation(OtherCompany) <= -100)
			{
				Company->SetHostilityTo(OtherCompany, true);
				if (OtherCompany == Game->GetPC()->GetCompany())
				{
					OtherCompany->SetHostilityTo(Company, true);
				}
			}
		}
	}
}

void UFlareAIBehavior::SimulatePirateBehavior()
{
	// Simulate company attitude towards others
	Company->GetAI()->UpdateDiplomacy();

	// Update trade routes
	int32 IdleCargoCapacity = Company->GetAI()->UpdateTrading();

	// Buy war ships
	Company->GetAI()->UpdateWarShipAcquisition();

	Company->GetAI()->UpdateMilitaryMovement(false);
}

void UFlareAIBehavior::GenerateAffilities()
{
	// Reset resource affilities
	ResourceAffilities.Empty();

	// Default behavior

	SetResourceAffilities(1.f);

	// If above 5, will become exclusive
	SetSectorAffilities(1.f);

	StationCapture = 1.f;
	TradingBuy = 1.f;
	TradingSell = 1.f;
	TradingBoth = 0.5f;

	ShipyardAffility = 1.0;
	ConsumerAffility = 1.0;
	MaintenanceAffility = 0.5;

	// Budjet
	BudgetTechnology = 1.0;
	BudgetMilitary = 1.0;
	BudgetStation = 1.0;
	BudgetTrade = 1.0;

	ArmySize = 5.0;
	Agressivity = 1.0;
	Bold = 1.0;
	Peaceful = 1.0;

	// Pirate base
	SetSectorAffility(ST->Boneyard, 0.f);

	if(Company == ST->Pirates)
	{
		// Pirates
		// -------
		//
		// Pirate don't trade, and are only interested in getting money by force
		// All resource affilities are set to 0
		// They never build station
		// They never research science

		// They have 2 mode : agressive or moderate

		// They need a shipyard, if they have a shipyard they will steal resources,
		// or buy one to build ship, and fill and defend their base
		// They always want graveyard

		// They need some cargo to buy resources for their shipyard and arsenal

		SetResourceAffilities(0.f);

		SetSectorAffilities(0.f);

		SetSectorAffility(ST->Boneyard, 10.f);

		ShipyardAffility = 0.0;
		ConsumerAffility = 0.0;
		MaintenanceAffility = 0.0;

		// Only buy
		TradingSell = 0.f;
		TradingBoth = 0.f;

		// Don't capture station. Change in recovery
		StationCapture = 0.f;

		ArmySize = 50.0;
		Agressivity = 10.0;
		Bold = 10.0;

		// Budjet
		BudgetTechnology = 0.0;
		BudgetMilitary = 1.0;
		BudgetStation = 0.0;
		BudgetTrade = 0.1;

	}
	else if(Company == ST->GhostWorksShipyards)
	{
		// Love Hela and don't like Nema

		ShipyardAffility = 5.0;

		SetSectorAffilitiesByMoon(ST->Nema,0.5f);
		SetSectorAffilitiesByMoon(ST->Hela, 6.f);

		// Budjet
		BudgetTechnology = 1.0;
		BudgetMilitary = 2.0;
		BudgetStation = 1.0;
		BudgetTrade = 1.0;
	}
	else if(Company == ST->MiningSyndicate)
	{
		// Mining specialist.
		// Love raw material
		// Like hard work
		SetResourceAffility(ST->Water, 10.f);
		SetResourceAffility(ST->Silica, 10.f);
		SetResourceAffility(ST->IronOxyde, 10.f);
		SetResourceAffility(ST->Hydrogen, 2.f);

		// Budjet
		BudgetTechnology = 1.0;
		BudgetMilitary = 1.0;
		BudgetStation = 2.0;
		BudgetTrade = 2.0;
	}
	else if(Company == ST->HelixFoundries)
	{
		// Like hard factory, and Anka.
		// Base at Outpost
		SetResourceAffility(ST->Steel, 10.f);
		SetResourceAffility(ST->Tools, 10.f);
		SetResourceAffility(ST->Tech, 5.f);
		SetSectorAffilitiesByMoon(ST->Anka, 6.f);
		SetSectorAffility(ST->Outpost, 10.f);

		// Budjet
		BudgetTechnology = 1.5;
		BudgetMilitary = 1.0;
		BudgetStation = 2.0;
		BudgetTrade = 2.0;
	}
	else if(Company == ST->Sunwatch)
	{
		// Like hard factory, and Anka.
		// Base at Outpost
		SetResourceAffility(ST->Fuel, 10.f);

		// Budjet
		BudgetTechnology = 1.0;
		BudgetMilitary = 1.0;
		BudgetStation = 2.0;
		BudgetTrade = 2.0;
	}
	else if(Company == ST->IonLane)
	{
		BudgetTechnology = 1.0;
		BudgetMilitary = 2.0;
		BudgetStation = 0.1;
		BudgetTrade = 2.0;

		ArmySize = 10.0;
		Agressivity = 2.0;
	}
	else if(Company == ST->UnitedFarmsChemicals)
	{
		// Like chemisty
		SetResourceAffility(ST->Food, 10.f);
		SetResourceAffility(ST->Carbon, 5.f);
		SetResourceAffility(ST->Methane, 5.f);

		// Budjet
		BudgetTechnology = 1.5;
		BudgetMilitary = 1.0;
		BudgetStation = 2.0;
		BudgetTrade = 2.0;
	}
	else if(Company == ST->NemaHeavyWorks)
	{
		// Like Nema and heavy work
		SetResourceAffility(ST->FleetSupply, 2.f);
		SetResourceAffility(ST->Steel, 5.f);
		SetResourceAffility(ST->Tools, 5.f);
		SetResourceAffility(ST->Tech, 5.f);
		SetSectorAffilitiesByMoon(ST->Nema, 5.f);

		ShipyardAffility = 3.0;

		// Budjet
		BudgetTechnology = 1.5;
		BudgetMilitary = 1.0;
		BudgetStation = 2.0;
		BudgetTrade = 2.0;
	}
	else if(Company == ST->AxisSupplies)
	{
		// Assure fleet supply disponibility
		SetResourceAffility(ST->FleetSupply, 2.f);
		SetResourceAffility(ST->Food, 2.f);

		ShipyardAffility = 0.0;
		ConsumerAffility = 5.0;
		MaintenanceAffility = 10.0;

		// Budjet
		BudgetTechnology = 1.0;
		BudgetMilitary = 0.5;
		BudgetStation = 2.0;
		BudgetTrade = 2.0;

		ArmySize = 1.0;
		Agressivity = 0.0;
		Peaceful = 10.0;
	}

}

void UFlareAIBehavior::SetResourceAffilities(float Value)
{
	for(int32 ResourceIndex = 0; ResourceIndex < Game->GetResourceCatalog()->Resources.Num(); ResourceIndex++)
	{
		FFlareResourceDescription* Resource = &Game->GetResourceCatalog()->Resources[ResourceIndex]->Data;
		SetResourceAffility(Resource, Value);
	}
}

void UFlareAIBehavior::SetResourceAffility(FFlareResourceDescription* Resource, float Value)
{
	if(ResourceAffilities.Contains(Resource)){
		ResourceAffilities[Resource] = Value;
	}
	else
	{
		ResourceAffilities.Add(Resource, Value);
	}
}

void UFlareAIBehavior::SetSectorAffilities(float Value)
{
	for(int32 SectorIndex = 0; SectorIndex < Game->GetGameWorld()->GetSectors().Num(); SectorIndex++)
	{
		UFlareSimulatedSector* Sector = Game->GetGameWorld()->GetSectors()[SectorIndex];
		SetSectorAffility(Sector, Value);
	}
}

void UFlareAIBehavior::SetSectorAffility(UFlareSimulatedSector* Sector, float Value)
{
	if(SectorAffilities.Contains(Sector)){
		SectorAffilities[Sector] = Value;
	}
	else
	{
		SectorAffilities.Add(Sector, Value);
	}
}

void UFlareAIBehavior::SetSectorAffilitiesByMoon(FFlareCelestialBody *CelestialBody, float Value)
{
	for(int32 SectorIndex = 0; SectorIndex < Game->GetGameWorld()->GetSectors().Num(); SectorIndex++)
	{
		UFlareSimulatedSector* Sector = Game->GetGameWorld()->GetSectors()[SectorIndex];

		if(Sector->GetOrbitParameters()->CelestialBodyIdentifier == CelestialBody->Identifier)
		{
			SetSectorAffility(Sector, Value);
		}
	}
}

/*----------------------------------------------------
	Getters
----------------------------------------------------*/

float UFlareAIBehavior::GetSectorAffility(UFlareSimulatedSector* Sector)
{
	if(SectorAffilities.Contains(Sector))
	{
		return SectorAffilities[Sector];
	}
	return 1.f;
}

float UFlareAIBehavior::GetResourceAffility(FFlareResourceDescription* Resource)
{
	if(ResourceAffilities.Contains(Resource))
	{
		return ResourceAffilities[Resource];
	}
	return 1.f;
}
<commit_msg>Pirate now want peace with Axis supplies to repair or refill everywhere<commit_after>#include "../../Flare.h"
#include "FlareAIBehavior.h"

#include "../FlareGame.h"
#include "../FlareCompany.h"
#include "../FlareScenarioTools.h"
#include "../../Spacecrafts/FlareSimulatedSpacecraft.h"

/*----------------------------------------------------
	Public API
----------------------------------------------------*/

UFlareAIBehavior::UFlareAIBehavior(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
}

void UFlareAIBehavior::Load(UFlareCompany* ParentCompany)
{
	if(!Company)
	{
		Company = ParentCompany;
		check(Company);
		Game = Company->GetGame();
		ST = Game->GetScenarioTools();
		check(ST);

		GenerateAffilities();
	}
}

void UFlareAIBehavior::Simulate()
{
	if(Company == ST->Pirates)
	{
		SimulatePirateBehavior();
	}
	else
	{
		SimulateGeneralBehavior();
	}

}

void UFlareAIBehavior::SimulateGeneralBehavior()
{
	// Update trade routes
	int32 IdleCargoCapacity = Company->GetAI()->UpdateTrading();

	// Create or upgrade stations
	Company->GetAI()->UpdateStationConstruction(IdleCargoCapacity);

	// Buy ships
	Company->GetAI()->UpdateShipAcquisition(IdleCargoCapacity);

	Company->GetAI()->UpdateMilitaryMovement(true);
}

void UFlareAIBehavior::UpdateDiplomacy()
{

	// Simulate company attitude towards others
	for (int32 CompanyIndex = 0; CompanyIndex < Game->GetGameWorld()->GetCompanies().Num(); CompanyIndex++)
	{
		UFlareCompany* OtherCompany = Game->GetGameWorld()->GetCompanies()[CompanyIndex];

		if (OtherCompany == Company)
		{
			continue;
		}

		if(Company == ST->Pirates && OtherCompany != ST->AxisSupplies)
		{
			Company->SetHostilityTo(OtherCompany, true);
		}
		else
		{
			if (Company->GetHostility(OtherCompany) == EFlareHostility::Hostile && Company->GetReputation(OtherCompany) > -100)
			{
				Company->SetHostilityTo(OtherCompany, false);
			}
			else if (Company->GetHostility(OtherCompany) != EFlareHostility::Hostile && Company->GetReputation(OtherCompany) <= -100)
			{
				Company->SetHostilityTo(OtherCompany, true);
				if (OtherCompany == Game->GetPC()->GetCompany())
				{
					OtherCompany->SetHostilityTo(Company, true);
				}
			}
		}
	}
}

void UFlareAIBehavior::SimulatePirateBehavior()
{
	// Simulate company attitude towards others
	Company->GetAI()->UpdateDiplomacy();

	// Update trade routes
	int32 IdleCargoCapacity = Company->GetAI()->UpdateTrading();

	// Buy war ships
	Company->GetAI()->UpdateWarShipAcquisition();

	Company->GetAI()->UpdateMilitaryMovement(false);
}

void UFlareAIBehavior::GenerateAffilities()
{
	// Reset resource affilities
	ResourceAffilities.Empty();

	// Default behavior

	SetResourceAffilities(1.f);

	// If above 5, will become exclusive
	SetSectorAffilities(1.f);

	StationCapture = 1.f;
	TradingBuy = 1.f;
	TradingSell = 1.f;
	TradingBoth = 0.5f;

	ShipyardAffility = 1.0;
	ConsumerAffility = 1.0;
	MaintenanceAffility = 0.5;

	// Budjet
	BudgetTechnology = 1.0;
	BudgetMilitary = 1.0;
	BudgetStation = 1.0;
	BudgetTrade = 1.0;

	ArmySize = 5.0;
	Agressivity = 1.0;
	Bold = 1.0;
	Peaceful = 1.0;

	// Pirate base
	SetSectorAffility(ST->Boneyard, 0.f);

	if(Company == ST->Pirates)
	{
		// Pirates
		// -------
		//
		// Pirate don't trade, and are only interested in getting money by force
		// All resource affilities are set to 0
		// They never build station
		// They never research science

		// They have 2 mode : agressive or moderate

		// They need a shipyard, if they have a shipyard they will steal resources,
		// or buy one to build ship, and fill and defend their base
		// They always want graveyard

		// They need some cargo to buy resources for their shipyard and arsenal

		SetResourceAffilities(0.f);

		SetSectorAffilities(0.f);

		SetSectorAffility(ST->Boneyard, 10.f);

		ShipyardAffility = 0.0;
		ConsumerAffility = 0.0;
		MaintenanceAffility = 0.0;

		// Only buy
		TradingSell = 0.f;
		TradingBoth = 0.f;

		// Don't capture station. Change in recovery
		StationCapture = 0.f;

		ArmySize = 50.0;
		Agressivity = 10.0;
		Bold = 10.0;

		// Budjet
		BudgetTechnology = 0.0;
		BudgetMilitary = 1.0;
		BudgetStation = 0.0;
		BudgetTrade = 0.1;

	}
	else if(Company == ST->GhostWorksShipyards)
	{
		// Love Hela and don't like Nema

		ShipyardAffility = 5.0;

		SetSectorAffilitiesByMoon(ST->Nema,0.5f);
		SetSectorAffilitiesByMoon(ST->Hela, 6.f);

		// Budjet
		BudgetTechnology = 1.0;
		BudgetMilitary = 2.0;
		BudgetStation = 1.0;
		BudgetTrade = 1.0;
	}
	else if(Company == ST->MiningSyndicate)
	{
		// Mining specialist.
		// Love raw material
		// Like hard work
		SetResourceAffility(ST->Water, 10.f);
		SetResourceAffility(ST->Silica, 10.f);
		SetResourceAffility(ST->IronOxyde, 10.f);
		SetResourceAffility(ST->Hydrogen, 2.f);

		// Budjet
		BudgetTechnology = 1.0;
		BudgetMilitary = 1.0;
		BudgetStation = 2.0;
		BudgetTrade = 2.0;
	}
	else if(Company == ST->HelixFoundries)
	{
		// Like hard factory, and Anka.
		// Base at Outpost
		SetResourceAffility(ST->Steel, 10.f);
		SetResourceAffility(ST->Tools, 10.f);
		SetResourceAffility(ST->Tech, 5.f);
		SetSectorAffilitiesByMoon(ST->Anka, 6.f);
		SetSectorAffility(ST->Outpost, 10.f);

		// Budjet
		BudgetTechnology = 1.5;
		BudgetMilitary = 1.0;
		BudgetStation = 2.0;
		BudgetTrade = 2.0;
	}
	else if(Company == ST->Sunwatch)
	{
		// Like hard factory, and Anka.
		// Base at Outpost
		SetResourceAffility(ST->Fuel, 10.f);

		// Budjet
		BudgetTechnology = 1.0;
		BudgetMilitary = 1.0;
		BudgetStation = 2.0;
		BudgetTrade = 2.0;
	}
	else if(Company == ST->IonLane)
	{
		BudgetTechnology = 1.0;
		BudgetMilitary = 2.0;
		BudgetStation = 0.1;
		BudgetTrade = 2.0;

		ArmySize = 10.0;
		Agressivity = 2.0;
	}
	else if(Company == ST->UnitedFarmsChemicals)
	{
		// Like chemisty
		SetResourceAffility(ST->Food, 10.f);
		SetResourceAffility(ST->Carbon, 5.f);
		SetResourceAffility(ST->Methane, 5.f);

		// Budjet
		BudgetTechnology = 1.5;
		BudgetMilitary = 1.0;
		BudgetStation = 2.0;
		BudgetTrade = 2.0;
	}
	else if(Company == ST->NemaHeavyWorks)
	{
		// Like Nema and heavy work
		SetResourceAffility(ST->FleetSupply, 2.f);
		SetResourceAffility(ST->Steel, 5.f);
		SetResourceAffility(ST->Tools, 5.f);
		SetResourceAffility(ST->Tech, 5.f);
		SetSectorAffilitiesByMoon(ST->Nema, 5.f);

		ShipyardAffility = 3.0;

		// Budjet
		BudgetTechnology = 1.5;
		BudgetMilitary = 1.0;
		BudgetStation = 2.0;
		BudgetTrade = 2.0;
	}
	else if(Company == ST->AxisSupplies)
	{
		// Assure fleet supply disponibility
		SetResourceAffility(ST->FleetSupply, 2.f);
		SetResourceAffility(ST->Food, 2.f);

		ShipyardAffility = 0.0;
		ConsumerAffility = 5.0;
		MaintenanceAffility = 10.0;

		// Budjet
		BudgetTechnology = 1.0;
		BudgetMilitary = 0.5;
		BudgetStation = 2.0;
		BudgetTrade = 2.0;

		ArmySize = 1.0;
		Agressivity = 0.0;
		Peaceful = 10.0;
	}

}

void UFlareAIBehavior::SetResourceAffilities(float Value)
{
	for(int32 ResourceIndex = 0; ResourceIndex < Game->GetResourceCatalog()->Resources.Num(); ResourceIndex++)
	{
		FFlareResourceDescription* Resource = &Game->GetResourceCatalog()->Resources[ResourceIndex]->Data;
		SetResourceAffility(Resource, Value);
	}
}

void UFlareAIBehavior::SetResourceAffility(FFlareResourceDescription* Resource, float Value)
{
	if(ResourceAffilities.Contains(Resource)){
		ResourceAffilities[Resource] = Value;
	}
	else
	{
		ResourceAffilities.Add(Resource, Value);
	}
}

void UFlareAIBehavior::SetSectorAffilities(float Value)
{
	for(int32 SectorIndex = 0; SectorIndex < Game->GetGameWorld()->GetSectors().Num(); SectorIndex++)
	{
		UFlareSimulatedSector* Sector = Game->GetGameWorld()->GetSectors()[SectorIndex];
		SetSectorAffility(Sector, Value);
	}
}

void UFlareAIBehavior::SetSectorAffility(UFlareSimulatedSector* Sector, float Value)
{
	if(SectorAffilities.Contains(Sector)){
		SectorAffilities[Sector] = Value;
	}
	else
	{
		SectorAffilities.Add(Sector, Value);
	}
}

void UFlareAIBehavior::SetSectorAffilitiesByMoon(FFlareCelestialBody *CelestialBody, float Value)
{
	for(int32 SectorIndex = 0; SectorIndex < Game->GetGameWorld()->GetSectors().Num(); SectorIndex++)
	{
		UFlareSimulatedSector* Sector = Game->GetGameWorld()->GetSectors()[SectorIndex];

		if(Sector->GetOrbitParameters()->CelestialBodyIdentifier == CelestialBody->Identifier)
		{
			SetSectorAffility(Sector, Value);
		}
	}
}

/*----------------------------------------------------
	Getters
----------------------------------------------------*/

float UFlareAIBehavior::GetSectorAffility(UFlareSimulatedSector* Sector)
{
	if(SectorAffilities.Contains(Sector))
	{
		return SectorAffilities[Sector];
	}
	return 1.f;
}

float UFlareAIBehavior::GetResourceAffility(FFlareResourceDescription* Resource)
{
	if(ResourceAffilities.Contains(Resource))
	{
		return ResourceAffilities[Resource];
	}
	return 1.f;
}
<|endoftext|>


<commit_before>/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
#ident "$Id$"
/*======
This file is part of PerconaFT.

Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.

    PerconaFT is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License, version 2,
    as published by the Free Software Foundation.

    PerconaFT 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 PerconaFT.  If not, see <http://www.gnu.org/licenses/>.

----------------------------------------

    PerconaFT is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License, version 3,
    as published by the Free Software Foundation.

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

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

#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."

#include "test.h"

#include "ft-cachetable-wrappers.h"
#include "ft-flusher.h"
#include "ft-flusher-internal.h"

static bool
dont_destroy_bn(void* UU(extra)) {
    return false;
}

static void merge_should_not_happen(struct flusher_advice* UU(fa),
                              FT UU(h),
                              FTNODE UU(parent),
                              int UU(childnum),
                              FTNODE UU(child),
                              void* UU(extra))
{
    assert(false);
}

static bool dont_recursively_flush(FTNODE UU(child), void* UU(extra)) {
    return false;
}

static int child_to_flush(FT UU(h), FTNODE parent, void* UU(extra)) {
    assert(parent->height == 2);
    assert(parent->n_children == 1);
    return 0;
}

static void dummy_update_status(FTNODE UU(child), int UU(dirtied), void* UU(extra)) {
}

enum { NODESIZE = 1024, KSIZE=NODESIZE-100, TOKU_PSIZE=20 };

static void test_oldest_referenced_xid_gets_propogated(void) {
    int r;
    CACHETABLE ct;
    FT_HANDLE t;
    BLOCKNUM grandchild_leaf_blocknum, child_nonleaf_blocknum, root_blocknum;

    toku_cachetable_create(&ct, 500*1024*1024, ZERO_LSN, nullptr);
    unlink("foo1.ft_handle");
    r = toku_open_ft_handle("foo1.ft_handle", 1, &t, NODESIZE, NODESIZE/2, TOKU_DEFAULT_COMPRESSION_METHOD, ct, nullptr, toku_builtin_compare_fun);
    assert(r==0);

    toku_testsetup_initialize();  // must precede any other toku_testsetup calls

    // This test flushes from a nonleaf root to a nonleaf child, without any leaf nodes.

    r = toku_testsetup_leaf(t, &grandchild_leaf_blocknum, 1, NULL, NULL);
    assert(r==0);

    r = toku_testsetup_nonleaf(t, 1, &child_nonleaf_blocknum, 1, &grandchild_leaf_blocknum, NULL, NULL);
    assert(r==0);

    r = toku_testsetup_nonleaf(t, 2, &root_blocknum, 1, &child_nonleaf_blocknum, NULL, NULL);
    assert(r==0);

    r = toku_testsetup_root(t, root_blocknum);
    assert(r==0);

    r = toku_testsetup_insert_to_nonleaf(
        t,
        root_blocknum,
        FT_INSERT,
        "a",
        2,
        NULL,
        0
        );

    // Verify that both the root and its child start with TXNID_NONE
    // for the oldest referenced xid

    // first verify the child
    FTNODE node = NULL;
    ftnode_fetch_extra bfe;
    bfe.create_for_min_read(t->ft);
    toku_pin_ftnode(
        t->ft,
        child_nonleaf_blocknum,
        toku_cachetable_hash(t->ft->cf, child_nonleaf_blocknum),
        &bfe,
        PL_WRITE_EXPENSIVE,
        &node,
        true
        );
    assert(node->height == 1);
    assert(node->n_children == 1);
    assert(BP_BLOCKNUM(node, 0).b == grandchild_leaf_blocknum.b);
    assert(node->oldest_referenced_xid_known == TXNID_NONE);
    toku_unpin_ftnode(t->ft, node);

    // now verify the root - keep it pinned so we can flush it below
    toku_pin_ftnode(
        t->ft,
        root_blocknum,
        toku_cachetable_hash(t->ft->cf, root_blocknum),
        &bfe,
        PL_WRITE_EXPENSIVE,
        &node,
        true
        );
    assert(node->height == 2);
    assert(node->n_children == 1);
    assert(BP_BLOCKNUM(node, 0).b == child_nonleaf_blocknum.b);
    assert(toku_bnc_nbytesinbuf(BNC(node, 0)) > 0);
    assert(node->oldest_referenced_xid_known == TXNID_NONE);

    // set the root's oldest referenced xid to something special
    const TXNID flush_xid = 25000;
    node->oldest_referenced_xid_known = flush_xid;

    // do the flush
    struct flusher_advice fa;
    flusher_advice_init(
        &fa,
        child_to_flush,
        dont_destroy_bn,
        dont_recursively_flush,
        merge_should_not_happen,
        dummy_update_status,
        default_pick_child_after_split,
        NULL
        );
    toku_ft_flush_some_child(t->ft, node, &fa);

    // pin the child, verify that oldest referenced xid was
    // propagated from parent to child during the flush
    toku_pin_ftnode(
        t->ft,
        child_nonleaf_blocknum,
        toku_cachetable_hash(t->ft->cf, child_nonleaf_blocknum),
        &bfe,
        PL_WRITE_EXPENSIVE,
        &node,
        true
        );
    assert(node->oldest_referenced_xid_known == flush_xid);

    toku_unpin_ftnode(t->ft, node);
    r = toku_close_ft_handle_nolsn(t, 0);    assert(r==0);
    toku_cachetable_close(&ct);
}

int test_main(int argc __attribute__((__unused__)), const char *argv[] __attribute__((__unused__))) {
    default_parse_args(argc, argv);
    test_oldest_referenced_xid_gets_propogated();
    return 0;
}
<commit_msg>Fix spelling to 'propagated' in function name ftest_oldest_referenced_xid_gets_propogated<commit_after>/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
#ident "$Id$"
/*======
This file is part of PerconaFT.

Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.

    PerconaFT is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License, version 2,
    as published by the Free Software Foundation.

    PerconaFT 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 PerconaFT.  If not, see <http://www.gnu.org/licenses/>.

----------------------------------------

    PerconaFT is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License, version 3,
    as published by the Free Software Foundation.

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

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

#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."

#include "test.h"

#include "ft-cachetable-wrappers.h"
#include "ft-flusher.h"
#include "ft-flusher-internal.h"

static bool
dont_destroy_bn(void* UU(extra)) {
    return false;
}

static void merge_should_not_happen(struct flusher_advice* UU(fa),
                              FT UU(h),
                              FTNODE UU(parent),
                              int UU(childnum),
                              FTNODE UU(child),
                              void* UU(extra))
{
    assert(false);
}

static bool dont_recursively_flush(FTNODE UU(child), void* UU(extra)) {
    return false;
}

static int child_to_flush(FT UU(h), FTNODE parent, void* UU(extra)) {
    assert(parent->height == 2);
    assert(parent->n_children == 1);
    return 0;
}

static void dummy_update_status(FTNODE UU(child), int UU(dirtied), void* UU(extra)) {
}

enum { NODESIZE = 1024, KSIZE=NODESIZE-100, TOKU_PSIZE=20 };

static void test_oldest_referenced_xid_gets_propagated(void) {
    int r;
    CACHETABLE ct;
    FT_HANDLE t;
    BLOCKNUM grandchild_leaf_blocknum, child_nonleaf_blocknum, root_blocknum;

    toku_cachetable_create(&ct, 500*1024*1024, ZERO_LSN, nullptr);
    unlink("foo1.ft_handle");
    r = toku_open_ft_handle("foo1.ft_handle", 1, &t, NODESIZE, NODESIZE/2, TOKU_DEFAULT_COMPRESSION_METHOD, ct, nullptr, toku_builtin_compare_fun);
    assert(r==0);

    toku_testsetup_initialize();  // must precede any other toku_testsetup calls

    // This test flushes from a nonleaf root to a nonleaf child, without any leaf nodes.

    r = toku_testsetup_leaf(t, &grandchild_leaf_blocknum, 1, NULL, NULL);
    assert(r==0);

    r = toku_testsetup_nonleaf(t, 1, &child_nonleaf_blocknum, 1, &grandchild_leaf_blocknum, NULL, NULL);
    assert(r==0);

    r = toku_testsetup_nonleaf(t, 2, &root_blocknum, 1, &child_nonleaf_blocknum, NULL, NULL);
    assert(r==0);

    r = toku_testsetup_root(t, root_blocknum);
    assert(r==0);

    r = toku_testsetup_insert_to_nonleaf(
        t,
        root_blocknum,
        FT_INSERT,
        "a",
        2,
        NULL,
        0
        );

    // Verify that both the root and its child start with TXNID_NONE
    // for the oldest referenced xid

    // first verify the child
    FTNODE node = NULL;
    ftnode_fetch_extra bfe;
    bfe.create_for_min_read(t->ft);
    toku_pin_ftnode(
        t->ft,
        child_nonleaf_blocknum,
        toku_cachetable_hash(t->ft->cf, child_nonleaf_blocknum),
        &bfe,
        PL_WRITE_EXPENSIVE,
        &node,
        true
        );
    assert(node->height == 1);
    assert(node->n_children == 1);
    assert(BP_BLOCKNUM(node, 0).b == grandchild_leaf_blocknum.b);
    assert(node->oldest_referenced_xid_known == TXNID_NONE);
    toku_unpin_ftnode(t->ft, node);

    // now verify the root - keep it pinned so we can flush it below
    toku_pin_ftnode(
        t->ft,
        root_blocknum,
        toku_cachetable_hash(t->ft->cf, root_blocknum),
        &bfe,
        PL_WRITE_EXPENSIVE,
        &node,
        true
        );
    assert(node->height == 2);
    assert(node->n_children == 1);
    assert(BP_BLOCKNUM(node, 0).b == child_nonleaf_blocknum.b);
    assert(toku_bnc_nbytesinbuf(BNC(node, 0)) > 0);
    assert(node->oldest_referenced_xid_known == TXNID_NONE);

    // set the root's oldest referenced xid to something special
    const TXNID flush_xid = 25000;
    node->oldest_referenced_xid_known = flush_xid;

    // do the flush
    struct flusher_advice fa;
    flusher_advice_init(
        &fa,
        child_to_flush,
        dont_destroy_bn,
        dont_recursively_flush,
        merge_should_not_happen,
        dummy_update_status,
        default_pick_child_after_split,
        NULL
        );
    toku_ft_flush_some_child(t->ft, node, &fa);

    // pin the child, verify that oldest referenced xid was
    // propagated from parent to child during the flush
    toku_pin_ftnode(
        t->ft,
        child_nonleaf_blocknum,
        toku_cachetable_hash(t->ft->cf, child_nonleaf_blocknum),
        &bfe,
        PL_WRITE_EXPENSIVE,
        &node,
        true
        );
    assert(node->oldest_referenced_xid_known == flush_xid);

    toku_unpin_ftnode(t->ft, node);
    r = toku_close_ft_handle_nolsn(t, 0);    assert(r==0);
    toku_cachetable_close(&ct);
}

int test_main(int argc __attribute__((__unused__)), const char *argv[] __attribute__((__unused__))) {
    default_parse_args(argc, argv);
    test_oldest_referenced_xid_gets_propagated();
    return 0;
}
<|endoftext|>