<commit_before>#include "handler.h"
#include "extern.h"
#include "room.h"
#include "being.h"
#include "monster.h"
#include "disease.h"
#include "obj_base_clothing.h"

typedef struct breath_struct {
    spellNumT dam_type;
    const char *to_notvict, *to_char, *to_vict;
    int engulfBeing(TBeing *vict);
    void engulfRoom(TBeing *ch);
    int attack(TBeing *attacker, TBeing *victim, int lag);
} Breath;

Breath frost_breath = {
  SPELL_FROST_BREATH,
  "$N is blasted by <C>jets of frost<z> from $n's breath!",
  "You deep-freeze $N!",
  "$n's breath is <C>FREEZING!!!<z>",
};

Breath fire_breath = {
  SPELL_FIRE_BREATH,
  "$N is engulfed in <R>searing flames<z> from $n's breath!",
  "Stick a fork in $N, $E's done!",
  "$n's breath incinerates you with a <R>blast of FIRE!!!<z>\a",
};

Breath acid_breath = {
  SPELL_ACID_BREATH,
  "$N is burned by the <G>noxious acidic fumes<z> from $n's breath!",
  "You do a litmus test on $N!",
  "<G>Noxious acidic fumes<z> from $n's breath surround you!!!",
};

Breath chlorine_breath = {
  SPELL_CHLORINE_BREATH,
  "$N is immersed in a <Y>cloud of chlorine gas<z> from $n's breath!",
  "You burp a smelly one at $N!",
  "You choke on the <Y>toxic clouds<z> of $n's breath!!!",
};

Breath lightning_breath = {
  SPELL_LIGHTNING_BREATH,
  "$N is struck by a crackling <W>bolt of lightning<z> from $n's breath!",
  "You give $N a little jolt!",
  "$n's breath is <W>ELECTRIFYING!!!<z>",
};

Breath dust_breath = {
  SPELL_DUST_BREATH,
  "$N is scoured by <O>gales of dust<z> from $n's breath!",
  "You sandblast $N!",
  "$n's breath <O>sands off<z> all your rough edges!!!",
};

int Breath::engulfBeing(TBeing *vict)
{
  switch (dam_type) {
    case SPELL_FROST_BREATH:        return vict->frostEngulfed();
    case SPELL_FIRE_BREATH:         return vict->flameEngulfed();
    case SPELL_ACID_BREATH:         return vict->acidEngulfed();
    case SPELL_CHLORINE_BREATH:     return vict->chlorineEngulfed();
    case SPELL_LIGHTNING_BREATH:    return vict->lightningEngulfed();
    case SPELL_DUST_BREATH:
    default:
      return 0;
  }
}

void Breath::engulfRoom(TBeing *ch)
{
  switch (dam_type) {
    case SPELL_FROST_BREATH:        return ch->freezeRoom();
    case SPELL_FIRE_BREATH:         return ch->flameRoom();
    case SPELL_ACID_BREATH:         return ch->acidRoom();
    case SPELL_CHLORINE_BREATH:     return ch->chlorineRoom();
    case SPELL_LIGHTNING_BREATH:
    case SPELL_DUST_BREATH:
    default:
      break;
  }
}

typedef struct dragon_struct {
  const int vnum;
  Breath &breath;
  const int lag;
} Dragon;

Dragon dragons[] = {
  {2107,  frost_breath,     5},
  {3416,  acid_breath,      4},
  {4796,  chlorine_breath,  5},
  {4822,  lightning_breath, 2},
  {4858,  lightning_breath, 6},
  {6843,  fire_breath,      5},
  {8962,  fire_breath,      5},
  {10395, fire_breath,      3},
  {10601, lightning_breath, 6},
  {11805, fire_breath,      3},
  {12401, frost_breath,     7},
  {12403, fire_breath,      7},
  {12404, acid_breath,      7},
  {12405, frost_breath,     7},
  {14360, frost_breath,     5},
  {14361, fire_breath,      3},
  {20400, frost_breath,     2},
  {20875, dust_breath,      5},
  {22517, frost_breath,     5},
  {23633, lightning_breath, 5},
  {27905, lightning_breath, 2},
  {0,     fire_breath,      0}, // sentinel
};

Dragon& find_dragon(TBeing *mob) {
  int i = -1;
  while (dragons[++i].vnum)
    if (dragons[i].vnum == mob->mobVnum())
      return dragons[i];
  return dragons[i];
}

// if player has shield, attempt to block breath weapon
int shield_absorb_damage(TBeing *vict, int dam)
{
  TThing *left, *right;
  TBaseClothing *shield = NULL;
  wearSlotT slot = WEAR_NOWHERE;

  left = vict->equipment[HOLD_LEFT];
  right = vict->equipment[HOLD_RIGHT];

  TBaseClothing *tbc = NULL;
  if (left &&
      (tbc = dynamic_cast<TBaseClothing *>(left)) &&
      tbc->isShield()) {
    shield = tbc;
    slot = HOLD_LEFT;
  } else if (right &&
      (tbc = dynamic_cast<TBaseClothing *>(right)) &&
      tbc->isShield()) {
    shield = tbc;
    slot = HOLD_RIGHT;
  }
  if (!shield)
    return dam;

  // shield will absorb 10 to 20 percent of its structure
  int shielddam=(int)((shield->getMaxStructPoints()/100.0) * ::number(10,20));
  dam=max(0, dam-(shielddam*5)); // each structure point = 5 hp

  act("You hold your $o up to block the blast.",TRUE,vict,shield,0,TO_CHAR);
  act("$n holds $s $o up to block the blast.",TRUE,vict,shield,0,TO_ROOM);

  bool destroyed = shielddam >= shield->getStructPoints();

  sstring msg = format("$p %s blocks the blast %s") %
        (dam ? "partially" : "completely") %
        (destroyed ? "but is utterly destroyed!" : "and is seriously damaged.");

  act(msg,TRUE,vict,shield,0,TO_CHAR);
  act(msg,TRUE,vict,shield,0,TO_ROOM);

  if (!(vict->roomp && vict->roomp->isRoomFlag(ROOM_ARENA))) {
    if (destroyed) {
      vict->unequip(slot);
      if (!shield->makeScraps())
        delete shield;
      shield = NULL;
    } else {
      shield->addToStructPoints(-shielddam);
    }
  }

  return dam;
}

// returns DELETE_VICT
int Breath::attack(TBeing *attacker, TBeing *victim, int lag)
{
  // This is pretty arbitrary, but keep an eye toward matching what
  // skillDam is going to do as this is effectively just a very special attack
  // let damage be 0.5 * lev * rnds of lag
  int dam = (int) (0.5 * attacker->GetMaxLevel() * lag);

  // slight randomization
  int random = (int) (0.20 * dam);
  dam += ::number(-random, random);

  act(to_notvict, TRUE, attacker, NULL, victim, TO_NOTVICT);
  act(to_char, TRUE, attacker, NULL, victim, TO_CHAR);
  act(to_vict, TRUE, attacker, NULL, victim, TO_VICT);

  if (!(dam=shield_absorb_damage(victim, dam)))
    return 1;

  attacker->reconcileHurt(victim, 0.1);

  int rc = engulfBeing(victim);
  if (IS_SET_DELETE(rc, DELETE_THIS))
    return DELETE_VICT;

  if (attacker->reconcileDamage(victim, dam, dam_type) == -1)
    return DELETE_VICT;

  return 1;
}

int DragonBreath(TBeing *, cmdTypeT cmd, const char *, TMonster *myself, TObj *)
{
  if (!myself || (cmd != CMD_MOB_COMBAT))
    return FALSE;
  if (!myself->fight() || !myself->fight()->sameRoom(*myself))
    return FALSE;
  if (!myself->awake())
    return FALSE;
  if (myself->getWait() > 0)
    return FALSE;

  Dragon &dragon = find_dragon(myself);

  if (!dragon.vnum) {
    // in general, this is bad, but dumn builders often "test"
    if (myself->number == -1)
      vlogf(LOG_LOW, format("Dragon (%s:%d) trying to breathe in room %d and not hard coded.") %
            myself->getName() % myself->mobVnum() % myself->inRoom());
    else
      vlogf(LOG_BUG, format("Dragon has no defined breath. (%d)") %  myself->mobVnum());
    return FALSE;
  }

  if (myself->hasDisease(DISEASE_DROWNING) ||
      myself->hasDisease(DISEASE_GARROTTE) ||
      myself->hasDisease(DISEASE_SUFFOCATE)) {
    myself->sendTo("ACK!!  Your present situation prevents you from breathing.\n\r");
    act("$n rears back...", 1, myself, 0, 0, TO_ROOM);
    act("Thank the deities $e is unable to breathe.", 1, myself, 0, 0, TO_ROOM);
    return FALSE;
  }

  act("$n rears back and inhales...", 1, myself, 0, 0, TO_ROOM);
  myself->addToWait(combatRound(1) * dragon.lag);

  // have all the mobs in the room try to run for it!
  for (StuffIter it=myself->roomp->stuff.begin();it!=myself->roomp->stuff.end();++it){
    TMonster *tm = dynamic_cast<TMonster *>(*it);
    if (tm && tm != myself && tm->canSee(myself) && ::number(0, 9))
      tm->doFlee("");
  }

  for (StuffIter it=myself->roomp->stuff.begin();it!=myself->roomp->stuff.end();++it){
    TBeing *tmp = dynamic_cast<TBeing *>(*it);
    if (!tmp || tmp == myself)
      continue;
    int rc = dragon.breath.attack(myself, tmp, dragon.lag);
    if (IS_SET_DELETE(rc, DELETE_VICT)) {
      delete tmp;
      tmp = NULL;
    }
  }

  dragon.breath.engulfRoom(myself);

  return TRUE;
}

void TBeing::doBreath(const char *argument)
{
  char buf[256];
  Breath breath;
  TBeing *vict;

  if (hasDisease(DISEASE_DROWNING) ||
      hasDisease(DISEASE_GARROTTE) ||
      hasDisease(DISEASE_SUFFOCATE)) {
    sendTo("ACK!!  Your present situation prevents you from breathing.\n\r");
    return;
  }

  if (!(isImmortal()) || !isPc()) {
    sendTo("You inhale and exhale deeply.  Feels good!\n\r");
    sendTo("Fortunately, your brain handles this for you automatically.\n\r");
    return;
  }

  if (!hasWizPower(POWER_BREATHE)) {
    sendTo("You lack the power to breathe dragonbreath.\n\r");
    return;
  }

  argument = one_argument(argument, buf, cElements(buf));
  if (!*buf) {
    sendTo("Syntax: breathe <acid | fire | frost | lightning | chlorine> <victim>\n\r");
    return;
  }

  if (is_abbrev(buf, "acid")) {
    breath = acid_breath;
  } else if (is_abbrev(buf, "fire")) {
    breath = fire_breath;
  } else if (is_abbrev(buf, "frost")) {
    breath = frost_breath;
  } else if (is_abbrev(buf, "lightning")) {
    breath = lightning_breath;
  } else if (is_abbrev(buf, "chlorine")) {
    breath = chlorine_breath;
  } else if (is_abbrev(buf, "dust")) {
    breath = dust_breath;
  } else {
    sendTo("Syntax: breathe <acid | fire | frost | lightning | chlorine> <victim>\n\r");
    return;
  }

  argument = one_argument(argument, buf, cElements(buf));
  if (!*buf) {
    sendTo("Breathe on whom?\n\r");
    return;
  }

  if (!(vict = get_char_room_vis(this, buf))) {
    sendTo("I don't see anyone here like that.\n\r");
    return;
  }

  if (vict == this) {
    sendTo("You sure must be bored to do something that dumb.\n\r");
    return;
  }

  act("You inhale deeply and turn to face $N...",TRUE,this,0,vict,TO_CHAR);
  act("$n inhales deeply and turns in your direction...",TRUE,this,0,vict,TO_VICT);
  act("$n inhales deeply and turns to face $N...",TRUE,this,0,vict,TO_NOTVICT);

  act("You exhale forcefully...",TRUE,this,0,vict,TO_CHAR);
  act("$e exhales forcefully...",TRUE,this,0,vict,TO_ROOM);

  int rc = breath.attack(this, vict, 1);
  if (IS_SET_DELETE(rc, DELETE_VICT)) {
    delete vict;
    vict = NULL;
  }

  breath.engulfRoom(this);
}
<commit_msg>Add comment pointing out Breath struct has methods<commit_after>#include "handler.h"
#include "extern.h"
#include "room.h"
#include "being.h"
#include "monster.h"
#include "disease.h"
#include "obj_base_clothing.h"

typedef struct breath_struct {
    spellNumT dam_type;
    const char *to_notvict;
    const char *to_char;
    const char *to_vict;

    // NOTE this struct has methods
    int engulfBeing(TBeing *vict);
    void engulfRoom(TBeing *ch);
    int attack(TBeing *attacker, TBeing *victim, int lag);
} Breath;

Breath frost_breath = {
  SPELL_FROST_BREATH,
  "$N is blasted by <C>jets of frost<z> from $n's breath!",
  "You deep-freeze $N!",
  "$n's breath is <C>FREEZING!!!<z>",
};

Breath fire_breath = {
  SPELL_FIRE_BREATH,
  "$N is engulfed in <R>searing flames<z> from $n's breath!",
  "Stick a fork in $N, $E's done!",
  "$n's breath incinerates you with a <R>blast of FIRE!!!<z>\a",
};

Breath acid_breath = {
  SPELL_ACID_BREATH,
  "$N is burned by the <G>noxious acidic fumes<z> from $n's breath!",
  "You do a litmus test on $N!",
  "<G>Noxious acidic fumes<z> from $n's breath surround you!!!",
};

Breath chlorine_breath = {
  SPELL_CHLORINE_BREATH,
  "$N is immersed in a <Y>cloud of chlorine gas<z> from $n's breath!",
  "You burp a smelly one at $N!",
  "You choke on the <Y>toxic clouds<z> of $n's breath!!!",
};

Breath lightning_breath = {
  SPELL_LIGHTNING_BREATH,
  "$N is struck by a crackling <W>bolt of lightning<z> from $n's breath!",
  "You give $N a little jolt!",
  "$n's breath is <W>ELECTRIFYING!!!<z>",
};

Breath dust_breath = {
  SPELL_DUST_BREATH,
  "$N is scoured by <O>gales of dust<z> from $n's breath!",
  "You sandblast $N!",
  "$n's breath <O>sands off<z> all your rough edges!!!",
};

int Breath::engulfBeing(TBeing *vict)
{
  switch (dam_type) {
    case SPELL_FROST_BREATH:        return vict->frostEngulfed();
    case SPELL_FIRE_BREATH:         return vict->flameEngulfed();
    case SPELL_ACID_BREATH:         return vict->acidEngulfed();
    case SPELL_CHLORINE_BREATH:     return vict->chlorineEngulfed();
    case SPELL_LIGHTNING_BREATH:    return vict->lightningEngulfed();
    case SPELL_DUST_BREATH:
    default:
      return 0;
  }
}

void Breath::engulfRoom(TBeing *ch)
{
  switch (dam_type) {
    case SPELL_FROST_BREATH:        return ch->freezeRoom();
    case SPELL_FIRE_BREATH:         return ch->flameRoom();
    case SPELL_ACID_BREATH:         return ch->acidRoom();
    case SPELL_CHLORINE_BREATH:     return ch->chlorineRoom();
    case SPELL_LIGHTNING_BREATH:
    case SPELL_DUST_BREATH:
    default:
      break;
  }
}

typedef struct dragon_struct {
  const int vnum;
  Breath &breath;
  const int lag;
} Dragon;

Dragon dragons[] = {
  {2107,  frost_breath,     5},
  {3416,  acid_breath,      4},
  {4796,  chlorine_breath,  5},
  {4822,  lightning_breath, 2},
  {4858,  lightning_breath, 6},
  {6843,  fire_breath,      5},
  {8962,  fire_breath,      5},
  {10395, fire_breath,      3},
  {10601, lightning_breath, 6},
  {11805, fire_breath,      3},
  {12401, frost_breath,     7},
  {12403, fire_breath,      7},
  {12404, acid_breath,      7},
  {12405, frost_breath,     7},
  {14360, frost_breath,     5},
  {14361, fire_breath,      3},
  {20400, frost_breath,     2},
  {20875, dust_breath,      5},
  {22517, frost_breath,     5},
  {23633, lightning_breath, 5},
  {27905, lightning_breath, 2},
  {0,     fire_breath,      0}, // sentinel
};

Dragon& find_dragon(TBeing *mob) {
  int i = -1;
  while (dragons[++i].vnum)
    if (dragons[i].vnum == mob->mobVnum())
      return dragons[i];
  return dragons[i];
}

// if player has shield, attempt to block breath weapon
int shield_absorb_damage(TBeing *vict, int dam)
{
  TThing *left, *right;
  TBaseClothing *shield = NULL;
  wearSlotT slot = WEAR_NOWHERE;

  left = vict->equipment[HOLD_LEFT];
  right = vict->equipment[HOLD_RIGHT];

  TBaseClothing *tbc = NULL;
  if (left &&
      (tbc = dynamic_cast<TBaseClothing *>(left)) &&
      tbc->isShield()) {
    shield = tbc;
    slot = HOLD_LEFT;
  } else if (right &&
      (tbc = dynamic_cast<TBaseClothing *>(right)) &&
      tbc->isShield()) {
    shield = tbc;
    slot = HOLD_RIGHT;
  }
  if (!shield)
    return dam;

  // shield will absorb 10 to 20 percent of its structure
  int shielddam=(int)((shield->getMaxStructPoints()/100.0) * ::number(10,20));
  dam=max(0, dam-(shielddam*5)); // each structure point = 5 hp

  act("You hold your $o up to block the blast.",TRUE,vict,shield,0,TO_CHAR);
  act("$n holds $s $o up to block the blast.",TRUE,vict,shield,0,TO_ROOM);

  bool destroyed = shielddam >= shield->getStructPoints();

  sstring msg = format("$p %s blocks the blast %s") %
        (dam ? "partially" : "completely") %
        (destroyed ? "but is utterly destroyed!" : "and is seriously damaged.");

  act(msg,TRUE,vict,shield,0,TO_CHAR);
  act(msg,TRUE,vict,shield,0,TO_ROOM);

  if (!(vict->roomp && vict->roomp->isRoomFlag(ROOM_ARENA))) {
    if (destroyed) {
      vict->unequip(slot);
      if (!shield->makeScraps())
        delete shield;
      shield = NULL;
    } else {
      shield->addToStructPoints(-shielddam);
    }
  }

  return dam;
}

// returns DELETE_VICT
int Breath::attack(TBeing *attacker, TBeing *victim, int lag)
{
  // This is pretty arbitrary, but keep an eye toward matching what
  // skillDam is going to do as this is effectively just a very special attack
  // let damage be 0.5 * lev * rnds of lag
  int dam = (int) (0.5 * attacker->GetMaxLevel() * lag);

  // slight randomization
  int random = (int) (0.20 * dam);
  dam += ::number(-random, random);

  act(to_notvict, TRUE, attacker, NULL, victim, TO_NOTVICT);
  act(to_char, TRUE, attacker, NULL, victim, TO_CHAR);
  act(to_vict, TRUE, attacker, NULL, victim, TO_VICT);

  if (!(dam=shield_absorb_damage(victim, dam)))
    return 1;

  attacker->reconcileHurt(victim, 0.1);

  int rc = engulfBeing(victim);
  if (IS_SET_DELETE(rc, DELETE_THIS))
    return DELETE_VICT;

  if (attacker->reconcileDamage(victim, dam, dam_type) == -1)
    return DELETE_VICT;

  return 1;
}

int DragonBreath(TBeing *, cmdTypeT cmd, const char *, TMonster *myself, TObj *)
{
  if (!myself || (cmd != CMD_MOB_COMBAT))
    return FALSE;
  if (!myself->fight() || !myself->fight()->sameRoom(*myself))
    return FALSE;
  if (!myself->awake())
    return FALSE;
  if (myself->getWait() > 0)
    return FALSE;

  Dragon &dragon = find_dragon(myself);

  if (!dragon.vnum) {
    // in general, this is bad, but dumn builders often "test"
    if (myself->number == -1)
      vlogf(LOG_LOW, format("Dragon (%s:%d) trying to breathe in room %d and not hard coded.") %
            myself->getName() % myself->mobVnum() % myself->inRoom());
    else
      vlogf(LOG_BUG, format("Dragon has no defined breath. (%d)") %  myself->mobVnum());
    return FALSE;
  }

  if (myself->hasDisease(DISEASE_DROWNING) ||
      myself->hasDisease(DISEASE_GARROTTE) ||
      myself->hasDisease(DISEASE_SUFFOCATE)) {
    myself->sendTo("ACK!!  Your present situation prevents you from breathing.\n\r");
    act("$n rears back...", 1, myself, 0, 0, TO_ROOM);
    act("Thank the deities $e is unable to breathe.", 1, myself, 0, 0, TO_ROOM);
    return FALSE;
  }

  act("$n rears back and inhales...", 1, myself, 0, 0, TO_ROOM);
  myself->addToWait(combatRound(1) * dragon.lag);

  // have all the mobs in the room try to run for it!
  for (StuffIter it=myself->roomp->stuff.begin();it!=myself->roomp->stuff.end();++it){
    TMonster *tm = dynamic_cast<TMonster *>(*it);
    if (tm && tm != myself && tm->canSee(myself) && ::number(0, 9))
      tm->doFlee("");
  }

  for (StuffIter it=myself->roomp->stuff.begin();it!=myself->roomp->stuff.end();++it){
    TBeing *tmp = dynamic_cast<TBeing *>(*it);
    if (!tmp || tmp == myself)
      continue;
    int rc = dragon.breath.attack(myself, tmp, dragon.lag);
    if (IS_SET_DELETE(rc, DELETE_VICT)) {
      delete tmp;
      tmp = NULL;
    }
  }

  dragon.breath.engulfRoom(myself);

  return TRUE;
}

void TBeing::doBreath(const char *argument)
{
  char buf[256];
  Breath breath;
  TBeing *vict;

  if (hasDisease(DISEASE_DROWNING) ||
      hasDisease(DISEASE_GARROTTE) ||
      hasDisease(DISEASE_SUFFOCATE)) {
    sendTo("ACK!!  Your present situation prevents you from breathing.\n\r");
    return;
  }

  if (!(isImmortal()) || !isPc()) {
    sendTo("You inhale and exhale deeply.  Feels good!\n\r");
    sendTo("Fortunately, your brain handles this for you automatically.\n\r");
    return;
  }

  if (!hasWizPower(POWER_BREATHE)) {
    sendTo("You lack the power to breathe dragonbreath.\n\r");
    return;
  }

  argument = one_argument(argument, buf, cElements(buf));
  if (!*buf) {
    sendTo("Syntax: breathe <acid | fire | frost | lightning | chlorine> <victim>\n\r");
    return;
  }

  if (is_abbrev(buf, "acid")) {
    breath = acid_breath;
  } else if (is_abbrev(buf, "fire")) {
    breath = fire_breath;
  } else if (is_abbrev(buf, "frost")) {
    breath = frost_breath;
  } else if (is_abbrev(buf, "lightning")) {
    breath = lightning_breath;
  } else if (is_abbrev(buf, "chlorine")) {
    breath = chlorine_breath;
  } else if (is_abbrev(buf, "dust")) {
    breath = dust_breath;
  } else {
    sendTo("Syntax: breathe <acid | fire | frost | lightning | chlorine> <victim>\n\r");
    return;
  }

  argument = one_argument(argument, buf, cElements(buf));
  if (!*buf) {
    sendTo("Breathe on whom?\n\r");
    return;
  }

  if (!(vict = get_char_room_vis(this, buf))) {
    sendTo("I don't see anyone here like that.\n\r");
    return;
  }

  if (vict == this) {
    sendTo("You sure must be bored to do something that dumb.\n\r");
    return;
  }

  act("You inhale deeply and turn to face $N...",TRUE,this,0,vict,TO_CHAR);
  act("$n inhales deeply and turns in your direction...",TRUE,this,0,vict,TO_VICT);
  act("$n inhales deeply and turns to face $N...",TRUE,this,0,vict,TO_NOTVICT);

  act("You exhale forcefully...",TRUE,this,0,vict,TO_CHAR);
  act("$e exhales forcefully...",TRUE,this,0,vict,TO_ROOM);

  int rc = breath.attack(this, vict, 1);
  if (IS_SET_DELETE(rc, DELETE_VICT)) {
    delete vict;
    vict = NULL;
  }

  breath.engulfRoom(this);
}
<|endoftext|>


<commit_before>#include "../../deps/catch/include/catch.hpp"
#include "../../include/tensor.hpp"

TEST_CASE("tensor", "[tensor]") {

	SECTION("tensor::operator()") {

		{
			sor::tensor<int, 4> tensor({ 5, 8, 2, 4 });
			REQUIRE(tensor(0) == 5);
			REQUIRE(tensor(1) == 8);
			REQUIRE(tensor(2) == 2);
			REQUIRE(tensor(3) == 4);
		}

		{
			sor::tensor<int, 3, 4> tensor({
				4, 5, 7, 2,
				45, 7, 23, 21,
				98, 3, 56, 12
			});
			REQUIRE(tensor(0, 0) == 4);
			REQUIRE(tensor(0, 1) == 5);
			REQUIRE(tensor(1 ,1) == 7);
			REQUIRE(tensor(1, 0) == 45);
			REQUIRE(tensor(2, 2) == 56);
			REQUIRE(tensor(2, 3) == 12);
		}

	}

}<commit_msg>Added tests to existing functionality<commit_after>#include "../../deps/catch/include/catch.hpp"
#include "../../include/tensor.hpp"

TEST_CASE("tensor", "[tensor]") {

	SECTION("rank") {
		using rank1 = sor::rank<sor::tensor<int, 1>>;
		using rank2 = sor::rank<sor::tensor<int, 3, 4>>;
		using rank3 = sor::rank<sor::tensor<int, 4, 2, 5>>;
		using rank4 = sor::rank<sor::tensor<int, 7, 1, 34, 56>>;
		REQUIRE(rank1::value == 1);
		REQUIRE(rank2::value == 2);
		REQUIRE(rank3::value == 3);
		REQUIRE(rank4::value == 4);
	}

	SECTION("extent") {
		using tensor = sor::tensor<int, 7, 1, 34>;
		using extent0 = sor::extent<tensor, 0>;
		using extent1 = sor::extent<tensor, 1>;
		using extent2 = sor::extent<tensor, 2>;
		REQUIRE(extent0::value == 7);
		REQUIRE(extent1::value == 1);
		REQUIRE(extent2::value == 34);
	}

	SECTION("tensor::operator()") {

		{
			sor::tensor<int, 4> const tensor({ 5, 8, 2, 4 });
			REQUIRE(tensor(0) == 5);
			REQUIRE(tensor(1) == 8);
			REQUIRE(tensor(2) == 2);
			REQUIRE(tensor(3) == 4);
		}

		{
			sor::tensor<int, 3, 4> const tensor({
				4, 5, 7, 2,
				45, 7, 23, 21,
				98, 3, 56, 12
			});
			REQUIRE(tensor(0, 0) == 4);
			REQUIRE(tensor(0, 1) == 5);
			REQUIRE(tensor(1 ,1) == 7);
			REQUIRE(tensor(1, 0) == 45);
			REQUIRE(tensor(2, 2) == 56);
			REQUIRE(tensor(2, 3) == 12);
		}

		{
			sor::tensor<int, 3, 4> tensor({
				4, 5, 7, 2,
				45, 7, 23, 21,
				98, 3, 56, 12
			});
			tensor(0, 0) = 1;
			tensor(0, 1) = 0;
			tensor(0, 2) = 4;
			tensor(2, 3) = 7;
			REQUIRE(tensor(0, 0) == 1);
			REQUIRE(tensor(0, 1) == 0);
			REQUIRE(tensor(0, 2) == 4);
			REQUIRE(tensor(1 ,1) == 7);
			REQUIRE(tensor(1, 0) == 45);
			REQUIRE(tensor(2, 2) == 56);
			REQUIRE(tensor(2, 3) == 7);
		}

	}

}<|endoftext|>


<commit_before>// Copyright 2016-2017 Francesco Biscani (bluescarni@gmail.com)
//
// This file is part of the mp++ library.
//
// 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/.

// std::index_sequence and std::make_index_sequence implementation, from:
// http://stackoverflow.com/questions/17424477/implementation-c14-make-integer-sequence

#ifndef MPPP_TEST_UTILS_HPP
#define MPPP_TEST_UTILS_HPP

#include <cassert>
#include <cstddef>
#include <cstdint>
#include <gmp.h>
#include <initializer_list>
#include <limits>
#include <locale>
#include <random>
#include <sstream>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>

#include <mp++/mp++.hpp>

namespace mppp_test
{

// std::index_sequence and std::make_index_sequence implementation for C++11. These are available
// in the std library in C++14. Implementation taken from:
// http://stackoverflow.com/questions/17424477/implementation-c14-make-integer-sequence
template <std::size_t... Ints>
struct index_sequence {
    using type = index_sequence;
    using value_type = std::size_t;
    static constexpr std::size_t size() noexcept
    {
        return sizeof...(Ints);
    }
};

inline namespace impl
{

template <class Sequence1, class Sequence2>
struct merge_and_renumber;

template <std::size_t... I1, std::size_t... I2>
struct merge_and_renumber<index_sequence<I1...>, index_sequence<I2...>>
    : index_sequence<I1..., (sizeof...(I1) + I2)...> {
};
}

template <std::size_t N>
struct make_index_sequence
    : merge_and_renumber<typename make_index_sequence<N / 2>::type, typename make_index_sequence<N - N / 2>::type> {
};

template <>
struct make_index_sequence<0> : index_sequence<> {
};
template <>
struct make_index_sequence<1> : index_sequence<0> {
};

inline namespace impl
{

template <typename T, typename F, std::size_t... Is>
void apply_to_each_item(T &&t, const F &f, index_sequence<Is...>)
{
    (void)std::initializer_list<int>{0, (void(f(std::get<Is>(std::forward<T>(t)))), 0)...};
}
}

// Tuple for_each(). Execute the functor f on each element of the input Tuple.
// https://isocpp.org/blog/2015/01/for-each-arg-eric-niebler
// https://www.reddit.com/r/cpp/comments/2tffv3/for_each_argumentsean_parent/
// https://www.reddit.com/r/cpp/comments/33b06v/for_each_in_tuple/
template <class Tuple, class F>
void tuple_for_each(Tuple &&t, const F &f)
{
    apply_to_each_item(std::forward<Tuple>(t), f,
                       make_index_sequence<std::tuple_size<typename std::decay<Tuple>::type>::value>{});
}

inline namespace impl
{

template <typename T>
struct is_integer {
    static const bool value = false;
};

template <std::size_t SSize>
struct is_integer<mppp::integer<SSize>> {
    static const bool value = true;
};

template <typename T, typename std::enable_if<mppp::is_integral<T>::value && mppp::is_signed<T>::value, int>::type = 0>
inline long long lex_cast_tr(T n)
{
    return static_cast<long long>(n);
}

template <typename T,
          typename std::enable_if<mppp::is_integral<T>::value && mppp::is_unsigned<T>::value, int>::type = 0>
inline unsigned long long lex_cast_tr(T n)
{
    return static_cast<unsigned long long>(n);
}

template <typename T, typename std::enable_if<is_integer<T>::value, int>::type = 0>
inline std::string lex_cast_tr(const T &x)
{
    return x.to_string();
}

template <std::size_t SSize>
inline std::string lex_cast_tr(const mppp::rational<SSize> &q)
{
    return q.to_string();
}

template <typename T, typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0>
inline T lex_cast_tr(const T &x)
{
    return x;
}
}

// Lexical cast: retrieve the string representation of input object x.
template <typename T>
inline std::string lex_cast(const T &x)
{
    std::ostringstream oss;
    oss.imbue(std::locale::classic());
    oss << lex_cast_tr(x);
    return oss.str();
}

inline std::string lex_cast(const mppp::mpz_raii &m)
{
    return mppp::mpz_to_str(&m.m_mpz);
}

inline std::string lex_cast(const mppp::mpq_raii &m)
{
    return mppp::rational<1>(&m.m_mpq).to_string();
}

#if defined(MPPP_HAVE_GCC_INT128)

inline std::string lex_cast(__uint128_t n)
{
    return mppp::to_string(n);
}

inline std::string lex_cast(__int128_t n)
{
    return mppp::to_string(n);
}

#endif

// Set mpz to random value with n limbs. Top limb is divided by div.
inline void random_integer(mppp::mpz_raii &m, unsigned n, std::mt19937 &rng, ::mp_limb_t div = 1u)
{
    if (!n) {
        ::mpz_set_ui(&m.m_mpz, 0);
        return;
    }
    MPPP_MAYBE_TLS mppp::mpz_raii tmp;
    std::uniform_int_distribution<::mp_limb_t> dist(0u, std::numeric_limits<::mp_limb_t>::max());
    // Set the first limb.
    ::mpz_set_str(&m.m_mpz, lex_cast((dist(rng) & GMP_NUMB_MASK) / div).c_str(), 10);
    for (unsigned i = 1u; i < n; ++i) {
        ::mpz_set_str(&tmp.m_mpz, lex_cast(dist(rng) & GMP_NUMB_MASK).c_str(), 10);
        ::mpz_mul_2exp(&m.m_mpz, &m.m_mpz, GMP_NUMB_BITS);
        ::mpz_add(&m.m_mpz, &m.m_mpz, &tmp.m_mpz);
    }
}

// Set mpq to random value with n limbs for num/den.
inline void random_rational(mppp::mpq_raii &m, unsigned n, std::mt19937 &rng)
{
    if (!n) {
        ::mpq_set_ui(&m.m_mpq, 0, 1);
        return;
    }
    MPPP_MAYBE_TLS mppp::mpz_raii tmp;
    std::uniform_int_distribution<::mp_limb_t> dist(0u, std::numeric_limits<::mp_limb_t>::max());
    // Set the first limb.
    ::mpz_set_str(mpq_numref(&m.m_mpq), lex_cast(dist(rng) & GMP_NUMB_MASK).c_str(), 10);
    ::mpz_set_str(mpq_denref(&m.m_mpq), lex_cast(dist(rng) & GMP_NUMB_MASK).c_str(), 10);
    for (unsigned i = 1u; i < n; ++i) {
        ::mpz_set_str(&tmp.m_mpz, lex_cast(dist(rng) & GMP_NUMB_MASK).c_str(), 10);
        ::mpz_mul_2exp(mpq_numref(&m.m_mpq), mpq_numref(&m.m_mpq), GMP_NUMB_BITS);
        ::mpz_add(mpq_numref(&m.m_mpq), mpq_numref(&m.m_mpq), &tmp.m_mpz);
        ::mpz_set_str(&tmp.m_mpz, lex_cast(dist(rng) & GMP_NUMB_MASK).c_str(), 10);
        ::mpz_mul_2exp(mpq_denref(&m.m_mpq), mpq_denref(&m.m_mpq), GMP_NUMB_BITS);
        ::mpz_add(mpq_denref(&m.m_mpq), mpq_denref(&m.m_mpq), &tmp.m_mpz);
    }
    // Take care of zero den.
    if (mpz_sgn(mpq_denref(&m.m_mpq)) == 0) {
        ::mpz_set_ui(mpq_denref(&m.m_mpq), 1);
    }
    ::mpq_canonicalize(&m.m_mpq);
}

// Set mpz to the max value with n limbs.
inline void max_integer(mppp::mpz_raii &m, unsigned n)
{
    if (!n) {
        ::mpz_set_ui(&m.m_mpz, 0);
        return;
    }
    MPPP_MAYBE_TLS mppp::mpz_raii tmp;
    // Set the first limb.
    ::mpz_set_str(&m.m_mpz, lex_cast(GMP_NUMB_MAX).c_str(), 10);
    for (unsigned i = 1u; i < n; ++i) {
        ::mpz_set_str(&tmp.m_mpz, lex_cast(GMP_NUMB_MAX).c_str(), 10);
        ::mpz_mul_2exp(&m.m_mpz, &m.m_mpz, GMP_NUMB_BITS);
        ::mpz_add(&m.m_mpz, &m.m_mpz, &tmp.m_mpz);
    }
}

// Uniform int distribution wrapper, from min to max value for type T.
template <typename T, typename = void>
struct integral_minmax_dist {
    integral_minmax_dist() : m_dist(std::numeric_limits<T>::min(), std::numeric_limits<T>::max()) {}
    template <typename E>
    T operator()(E &e)
    {
        return m_dist(e);
    }
    std::uniform_int_distribution<T> m_dist;
};

// NOTE: char types are not supported in uniform_int_distribution by the standard.
// Use a small wrapper to get an int distribution instead, with the min max limits
// from the char type. We will be casting back when using the distribution.
template <typename T>
struct integral_minmax_dist<
    T, typename std::enable_if<std::is_same<char, T>::value || std::is_same<signed char, T>::value
                               || std::is_same<unsigned char, T>::value || std::is_same<wchar_t, T>::value>::type> {
    // Just gonna assume long/ulong can represent wchar_t here.
    using type = typename std::conditional<std::is_signed<T>::value, long, unsigned long>::type;
    static_assert(!std::is_same<T, wchar_t>::value
                      || (std::numeric_limits<T>::max() <= std::numeric_limits<type>::max()
                          && std::numeric_limits<T>::min() >= std::numeric_limits<type>::min()),
                  "Invalid range for wchar_t.");
    integral_minmax_dist() : m_dist(std::numeric_limits<T>::min(), std::numeric_limits<T>::max()) {}
    template <typename E>
    T operator()(E &e)
    {
        return static_cast<T>(m_dist(e));
    }
    std::uniform_int_distribution<type> m_dist;
};

#if defined(MPPP_HAVE_GCC_INT128)

template <>
struct integral_minmax_dist<__uint128_t, void> {
    integral_minmax_dist() : m_dist(0u, static_cast<std::uint_least64_t>(__uint128_t(-1) >> 64)) {}
    template <typename E>
    __uint128_t operator()(E &e)
    {
        return m_dist(e) + (__uint128_t(m_dist(e)) << 64);
    }
    std::uniform_int_distribution<std::uint_least64_t> m_dist;
};

template <>
struct integral_minmax_dist<__int128_t, void> : integral_minmax_dist<__uint128_t, void> {
    using integral_minmax_dist<__uint128_t, void>::integral_minmax_dist;
    template <typename E>
    __int128_t operator()(E &e)
    {
        return static_cast<__int128_t>(static_cast<integral_minmax_dist<__uint128_t, void> *>(this)->operator()(e));
    }
};

#endif
}

// A macro for checking that an expression throws a specific exception object satisfying a predicate.
#define REQUIRE_THROWS_PREDICATE(expr, exc, pred)                                                                      \
    {                                                                                \
        bool thrown_checked = false;                                                                                \
        bool pred_checked = false;                                                                                \
        try {                                                                                \
            (void)(expr);                                                                                \
        } catch (const exc &e) {                                                                                \
            thrown_checked = true;                                                                                \
            if (pred(e)) {                                                                                \
                pred_checked = true;                                                                                \
            }                                                                                \
        }                                                                                \
        REQUIRE(thrown_checked);                                                                                \
        REQUIRE(pred_checked);                                                                                \
    }

#endif
<commit_msg>Small test code simplification.<commit_after>// Copyright 2016-2017 Francesco Biscani (bluescarni@gmail.com)
//
// This file is part of the mp++ library.
//
// 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/.

// std::index_sequence and std::make_index_sequence implementation, from:
// http://stackoverflow.com/questions/17424477/implementation-c14-make-integer-sequence

#ifndef MPPP_TEST_UTILS_HPP
#define MPPP_TEST_UTILS_HPP

#include <cassert>
#include <cstddef>
#include <cstdint>
#include <gmp.h>
#include <initializer_list>
#include <limits>
#include <locale>
#include <random>
#include <sstream>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>

#include <mp++/mp++.hpp>

namespace mppp_test
{

// std::index_sequence and std::make_index_sequence implementation for C++11. These are available
// in the std library in C++14. Implementation taken from:
// http://stackoverflow.com/questions/17424477/implementation-c14-make-integer-sequence
template <std::size_t... Ints>
struct index_sequence {
    using type = index_sequence;
    using value_type = std::size_t;
    static constexpr std::size_t size() noexcept
    {
        return sizeof...(Ints);
    }
};

inline namespace impl
{

template <class Sequence1, class Sequence2>
struct merge_and_renumber;

template <std::size_t... I1, std::size_t... I2>
struct merge_and_renumber<index_sequence<I1...>, index_sequence<I2...>>
    : index_sequence<I1..., (sizeof...(I1) + I2)...> {
};
}

template <std::size_t N>
struct make_index_sequence
    : merge_and_renumber<typename make_index_sequence<N / 2>::type, typename make_index_sequence<N - N / 2>::type> {
};

template <>
struct make_index_sequence<0> : index_sequence<> {
};
template <>
struct make_index_sequence<1> : index_sequence<0> {
};

inline namespace impl
{

template <typename T, typename F, std::size_t... Is>
void apply_to_each_item(T &&t, const F &f, index_sequence<Is...>)
{
    (void)std::initializer_list<int>{0, (void(f(std::get<Is>(std::forward<T>(t)))), 0)...};
}
}

// Tuple for_each(). Execute the functor f on each element of the input Tuple.
// https://isocpp.org/blog/2015/01/for-each-arg-eric-niebler
// https://www.reddit.com/r/cpp/comments/2tffv3/for_each_argumentsean_parent/
// https://www.reddit.com/r/cpp/comments/33b06v/for_each_in_tuple/
template <class Tuple, class F>
void tuple_for_each(Tuple &&t, const F &f)
{
    apply_to_each_item(std::forward<Tuple>(t), f,
                       make_index_sequence<std::tuple_size<typename std::decay<Tuple>::type>::value>{});
}

inline namespace impl
{

template <typename T, typename std::enable_if<mppp::is_integral<T>::value && mppp::is_signed<T>::value, int>::type = 0>
inline long long lex_cast_tr(T n)
{
    return static_cast<long long>(n);
}

template <typename T,
          typename std::enable_if<mppp::is_integral<T>::value && mppp::is_unsigned<T>::value, int>::type = 0>
inline unsigned long long lex_cast_tr(T n)
{
    return static_cast<unsigned long long>(n);
}

template <std::size_t SSize>
inline std::string lex_cast_tr(const mppp::integer<SSize> &n)
{
    return n.to_string();
}

template <std::size_t SSize>
inline std::string lex_cast_tr(const mppp::rational<SSize> &q)
{
    return q.to_string();
}

template <typename T, typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0>
inline T lex_cast_tr(const T &x)
{
    return x;
}
}

// Lexical cast: retrieve the string representation of input object x.
template <typename T>
inline std::string lex_cast(const T &x)
{
    std::ostringstream oss;
    oss.imbue(std::locale::classic());
    oss << lex_cast_tr(x);
    return oss.str();
}

inline std::string lex_cast(const mppp::mpz_raii &m)
{
    return mppp::mpz_to_str(&m.m_mpz);
}

inline std::string lex_cast(const mppp::mpq_raii &m)
{
    return mppp::rational<1>(&m.m_mpq).to_string();
}

#if defined(MPPP_HAVE_GCC_INT128)

inline std::string lex_cast(__uint128_t n)
{
    return mppp::to_string(n);
}

inline std::string lex_cast(__int128_t n)
{
    return mppp::to_string(n);
}

#endif

// Set mpz to random value with n limbs. Top limb is divided by div.
inline void random_integer(mppp::mpz_raii &m, unsigned n, std::mt19937 &rng, ::mp_limb_t div = 1u)
{
    if (!n) {
        ::mpz_set_ui(&m.m_mpz, 0);
        return;
    }
    MPPP_MAYBE_TLS mppp::mpz_raii tmp;
    std::uniform_int_distribution<::mp_limb_t> dist(0u, std::numeric_limits<::mp_limb_t>::max());
    // Set the first limb.
    ::mpz_set_str(&m.m_mpz, lex_cast((dist(rng) & GMP_NUMB_MASK) / div).c_str(), 10);
    for (unsigned i = 1u; i < n; ++i) {
        ::mpz_set_str(&tmp.m_mpz, lex_cast(dist(rng) & GMP_NUMB_MASK).c_str(), 10);
        ::mpz_mul_2exp(&m.m_mpz, &m.m_mpz, GMP_NUMB_BITS);
        ::mpz_add(&m.m_mpz, &m.m_mpz, &tmp.m_mpz);
    }
}

// Set mpq to random value with n limbs for num/den.
inline void random_rational(mppp::mpq_raii &m, unsigned n, std::mt19937 &rng)
{
    if (!n) {
        ::mpq_set_ui(&m.m_mpq, 0, 1);
        return;
    }
    MPPP_MAYBE_TLS mppp::mpz_raii tmp;
    std::uniform_int_distribution<::mp_limb_t> dist(0u, std::numeric_limits<::mp_limb_t>::max());
    // Set the first limb.
    ::mpz_set_str(mpq_numref(&m.m_mpq), lex_cast(dist(rng) & GMP_NUMB_MASK).c_str(), 10);
    ::mpz_set_str(mpq_denref(&m.m_mpq), lex_cast(dist(rng) & GMP_NUMB_MASK).c_str(), 10);
    for (unsigned i = 1u; i < n; ++i) {
        ::mpz_set_str(&tmp.m_mpz, lex_cast(dist(rng) & GMP_NUMB_MASK).c_str(), 10);
        ::mpz_mul_2exp(mpq_numref(&m.m_mpq), mpq_numref(&m.m_mpq), GMP_NUMB_BITS);
        ::mpz_add(mpq_numref(&m.m_mpq), mpq_numref(&m.m_mpq), &tmp.m_mpz);
        ::mpz_set_str(&tmp.m_mpz, lex_cast(dist(rng) & GMP_NUMB_MASK).c_str(), 10);
        ::mpz_mul_2exp(mpq_denref(&m.m_mpq), mpq_denref(&m.m_mpq), GMP_NUMB_BITS);
        ::mpz_add(mpq_denref(&m.m_mpq), mpq_denref(&m.m_mpq), &tmp.m_mpz);
    }
    // Take care of zero den.
    if (mpz_sgn(mpq_denref(&m.m_mpq)) == 0) {
        ::mpz_set_ui(mpq_denref(&m.m_mpq), 1);
    }
    ::mpq_canonicalize(&m.m_mpq);
}

// Set mpz to the max value with n limbs.
inline void max_integer(mppp::mpz_raii &m, unsigned n)
{
    if (!n) {
        ::mpz_set_ui(&m.m_mpz, 0);
        return;
    }
    MPPP_MAYBE_TLS mppp::mpz_raii tmp;
    // Set the first limb.
    ::mpz_set_str(&m.m_mpz, lex_cast(GMP_NUMB_MAX).c_str(), 10);
    for (unsigned i = 1u; i < n; ++i) {
        ::mpz_set_str(&tmp.m_mpz, lex_cast(GMP_NUMB_MAX).c_str(), 10);
        ::mpz_mul_2exp(&m.m_mpz, &m.m_mpz, GMP_NUMB_BITS);
        ::mpz_add(&m.m_mpz, &m.m_mpz, &tmp.m_mpz);
    }
}

// Uniform int distribution wrapper, from min to max value for type T.
template <typename T, typename = void>
struct integral_minmax_dist {
    integral_minmax_dist() : m_dist(std::numeric_limits<T>::min(), std::numeric_limits<T>::max()) {}
    template <typename E>
    T operator()(E &e)
    {
        return m_dist(e);
    }
    std::uniform_int_distribution<T> m_dist;
};

// NOTE: char types are not supported in uniform_int_distribution by the standard.
// Use a small wrapper to get an int distribution instead, with the min max limits
// from the char type. We will be casting back when using the distribution.
template <typename T>
struct integral_minmax_dist<
    T, typename std::enable_if<std::is_same<char, T>::value || std::is_same<signed char, T>::value
                               || std::is_same<unsigned char, T>::value || std::is_same<wchar_t, T>::value>::type> {
    // Just gonna assume long/ulong can represent wchar_t here.
    using type = typename std::conditional<std::is_signed<T>::value, long, unsigned long>::type;
    static_assert(!std::is_same<T, wchar_t>::value
                      || (std::numeric_limits<T>::max() <= std::numeric_limits<type>::max()
                          && std::numeric_limits<T>::min() >= std::numeric_limits<type>::min()),
                  "Invalid range for wchar_t.");
    integral_minmax_dist() : m_dist(std::numeric_limits<T>::min(), std::numeric_limits<T>::max()) {}
    template <typename E>
    T operator()(E &e)
    {
        return static_cast<T>(m_dist(e));
    }
    std::uniform_int_distribution<type> m_dist;
};

#if defined(MPPP_HAVE_GCC_INT128)

template <>
struct integral_minmax_dist<__uint128_t, void> {
    integral_minmax_dist() : m_dist(0u, static_cast<std::uint_least64_t>(__uint128_t(-1) >> 64)) {}
    template <typename E>
    __uint128_t operator()(E &e)
    {
        return m_dist(e) + (__uint128_t(m_dist(e)) << 64);
    }
    std::uniform_int_distribution<std::uint_least64_t> m_dist;
};

template <>
struct integral_minmax_dist<__int128_t, void> : integral_minmax_dist<__uint128_t, void> {
    using integral_minmax_dist<__uint128_t, void>::integral_minmax_dist;
    template <typename E>
    __int128_t operator()(E &e)
    {
        return static_cast<__int128_t>(static_cast<integral_minmax_dist<__uint128_t, void> *>(this)->operator()(e));
    }
};

#endif
}

// A macro for checking that an expression throws a specific exception object satisfying a predicate.
#define REQUIRE_THROWS_PREDICATE(expr, exc, pred)                                                                      \
    {                                                                                \
        bool thrown_checked = false;                                                                                \
        bool pred_checked = false;                                                                                \
        try {                                                                                \
            (void)(expr);                                                                                \
        } catch (const exc &e) {                                                                                \
            thrown_checked = true;                                                                                \
            if (pred(e)) {                                                                                \
                pred_checked = true;                                                                                \
            }                                                                                \
        }                                                                                \
        REQUIRE(thrown_checked);                                                                                \
        REQUIRE(pred_checked);                                                                                \
    }

#endif
<|endoftext|>


<commit_before>#include "testquazip.h"

#include "qztest.h"

#include <QDir>
#include <QFileInfo>
#include <QHash>

#include <QtTest/QtTest>

#include <quazip/quazip.h>
#include <quazip/JlCompress.h>

void TestQuaZip::getFileList_data()
{
    QTest::addColumn<QString>("zipName");
    QTest::addColumn<QStringList>("fileNames");
    QTest::newRow("simple") << "qzfilelist.zip" << (
            QStringList() << "test0.txt" << "testdir1/test1.txt"
            << "testdir2/test2.txt" << "testdir2/subdir/test2sub.txt");
}

void TestQuaZip::getFileList()
{
    QFETCH(QString, zipName);
    QFETCH(QStringList, fileNames);
    qSort(fileNames);
    QDir curDir;
    if (curDir.exists(zipName)) {
        if (!curDir.remove(zipName))
            QFAIL("Can't remove zip file");
    }
    if (!createTestFiles(fileNames)) {
        QFAIL("Can't create test file");
    }
    if (!createTestArchive(zipName, fileNames)) {
        QFAIL("Can't create test archive");
    }
    QuaZip testZip(zipName);
    QVERIFY(testZip.open(QuaZip::mdUnzip));
    QVERIFY(testZip.goToFirstFile());
    QString firstFile = testZip.getCurrentFileName();
    QStringList fileList = testZip.getFileNameList();
    qSort(fileList);
    QCOMPARE(fileList, fileNames);
    QHash<QString, QFileInfo> srcInfo;
    foreach (QString fileName, fileNames) {
        srcInfo[fileName] = QFileInfo("tmp/" + fileName);
    }
    QList<QuaZipFileInfo> destList = testZip.getFileInfoList();
    QCOMPARE(destList.size(), srcInfo.size());
    for (int i = 0; i < destList.size(); i++) {
        QCOMPARE(static_cast<qint64>(destList[i].uncompressedSize),
                srcInfo[destList[i].name].size());
    }
    // test that we didn't mess up the current file
    QCOMPARE(testZip.getCurrentFileName(), firstFile);
    testZip.close();
    // clean up
    removeTestFiles(fileNames);
    curDir.remove(zipName);
}

void TestQuaZip::add_data()
{
    QTest::addColumn<QString>("zipName");
    QTest::addColumn<QStringList>("fileNames");
    QTest::addColumn<QStringList>("fileNamesToAdd");
    QTest::newRow("simple") << "qzadd.zip" << (
            QStringList() << "test0.txt" << "testdir1/test1.txt"
            << "testdir2/test2.txt" << "testdir2/subdir/test2sub.txt")
            << (QStringList() << "testAdd.txt");
}

void TestQuaZip::add()
{
    QFETCH(QString, zipName);
    QFETCH(QStringList, fileNames);
    QFETCH(QStringList, fileNamesToAdd);
    QDir curDir;
    if (curDir.exists(zipName)) {
        if (!curDir.remove(zipName))
            QFAIL("Can't remove zip file");
    }
    if (!createTestFiles(fileNames)) {
        QFAIL("Can't create test file");
    }
    if (!createTestArchive(zipName, fileNames)) {
        QFAIL("Can't create test archive");
    }
    if (!createTestFiles(fileNamesToAdd)) {
        QFAIL("Can't create test files to add");
    }
    QuaZip testZip(zipName);
    QVERIFY(testZip.open(QuaZip::mdUnzip));
    // according to the bug #3485459 the global is lost, so we test it
    QString globalComment = testZip.getComment();
    testZip.close();
    QVERIFY(testZip.open(QuaZip::mdAdd));
    foreach (QString fileName, fileNamesToAdd) {
        QuaZipFile testFile(&testZip);
        QVERIFY(testFile.open(QIODevice::WriteOnly,
            QuaZipNewInfo(fileName, "tmp/" + fileName)));
        QFile inFile("tmp/" + fileName);
        QVERIFY(inFile.open(QIODevice::ReadOnly));
        testFile.write(inFile.readAll());
        inFile.close();
        testFile.close();
    }
    testZip.close();
    QVERIFY(testZip.open(QuaZip::mdUnzip));
    QCOMPARE(testZip.getFileNameList(), fileNames + fileNamesToAdd);
    QCOMPARE(testZip.getComment(), globalComment);
    testZip.close();
    removeTestFiles(fileNames);
    removeTestFiles(fileNamesToAdd);
    curDir.remove(zipName);
}
<commit_msg>Add some tests for i18n file names<commit_after>#include "testquazip.h"

#include "qztest.h"

#include <QDir>
#include <QFileInfo>
#include <QHash>

#include <QtTest/QtTest>

#include <quazip/quazip.h>
#include <quazip/JlCompress.h>

void TestQuaZip::getFileList_data()
{
    QTest::addColumn<QString>("zipName");
    QTest::addColumn<QStringList>("fileNames");
    QTest::newRow("simple") << "qzfilelist.zip" << (
            QStringList() << "test0.txt" << "testdir1/test1.txt"
            << "testdir2/test2.txt" << "testdir2/subdir/test2sub.txt");
    QTest::newRow("russian") << QString::fromUtf8("файл.zip") << (
        QStringList() << QString::fromUtf8("test0.txt") << QString::fromUtf8("test1/test1.txt")
            << "testdir2/test2.txt" << "testdir2/subdir/test2sub.txt");
    QTest::newRow("japanese") << QString::fromUtf8("テスト.zip") << (
        QStringList() << QString::fromUtf8("test.txt"));
    QTest::newRow("hebrew") << QString::fromUtf8("פתח תקווה.zip") << (
        QStringList() << QString::fromUtf8("test.txt"));
}

void TestQuaZip::getFileList()
{
    QFETCH(QString, zipName);
    QFETCH(QStringList, fileNames);
    qSort(fileNames);
    QDir curDir;
    if (curDir.exists(zipName)) {
        if (!curDir.remove(zipName))
            QFAIL("Can't remove zip file");
    }
    if (!createTestFiles(fileNames)) {
        QFAIL("Can't create test file");
    }
    if (!createTestArchive(zipName, fileNames)) {
        QFAIL("Can't create test archive");
    }
    QuaZip testZip(zipName);
    QVERIFY(testZip.open(QuaZip::mdUnzip));
    QVERIFY(testZip.goToFirstFile());
    QString firstFile = testZip.getCurrentFileName();
    QStringList fileList = testZip.getFileNameList();
    qSort(fileList);
    QCOMPARE(fileList, fileNames);
    QHash<QString, QFileInfo> srcInfo;
    foreach (QString fileName, fileNames) {
        srcInfo[fileName] = QFileInfo("tmp/" + fileName);
    }
    QList<QuaZipFileInfo> destList = testZip.getFileInfoList();
    QCOMPARE(destList.size(), srcInfo.size());
    for (int i = 0; i < destList.size(); i++) {
        QCOMPARE(static_cast<qint64>(destList[i].uncompressedSize),
                srcInfo[destList[i].name].size());
    }
    // test that we didn't mess up the current file
    QCOMPARE(testZip.getCurrentFileName(), firstFile);
    testZip.close();
    // clean up
    removeTestFiles(fileNames);
    curDir.remove(zipName);
}

void TestQuaZip::add_data()
{
    QTest::addColumn<QString>("zipName");
    QTest::addColumn<QStringList>("fileNames");
    QTest::addColumn<QStringList>("fileNamesToAdd");
    QTest::newRow("simple") << "qzadd.zip" << (
            QStringList() << "test0.txt" << "testdir1/test1.txt"
            << "testdir2/test2.txt" << "testdir2/subdir/test2sub.txt")
            << (QStringList() << "testAdd.txt");
}

void TestQuaZip::add()
{
    QFETCH(QString, zipName);
    QFETCH(QStringList, fileNames);
    QFETCH(QStringList, fileNamesToAdd);
    QDir curDir;
    if (curDir.exists(zipName)) {
        if (!curDir.remove(zipName))
            QFAIL("Can't remove zip file");
    }
    if (!createTestFiles(fileNames)) {
        QFAIL("Can't create test file");
    }
    if (!createTestArchive(zipName, fileNames)) {
        QFAIL("Can't create test archive");
    }
    if (!createTestFiles(fileNamesToAdd)) {
        QFAIL("Can't create test files to add");
    }
    QuaZip testZip(zipName);
    QVERIFY(testZip.open(QuaZip::mdUnzip));
    // according to the bug #3485459 the global is lost, so we test it
    QString globalComment = testZip.getComment();
    testZip.close();
    QVERIFY(testZip.open(QuaZip::mdAdd));
    foreach (QString fileName, fileNamesToAdd) {
        QuaZipFile testFile(&testZip);
        QVERIFY(testFile.open(QIODevice::WriteOnly,
            QuaZipNewInfo(fileName, "tmp/" + fileName)));
        QFile inFile("tmp/" + fileName);
        QVERIFY(inFile.open(QIODevice::ReadOnly));
        testFile.write(inFile.readAll());
        inFile.close();
        testFile.close();
    }
    testZip.close();
    QVERIFY(testZip.open(QuaZip::mdUnzip));
    QCOMPARE(testZip.getFileNameList(), fileNames + fileNamesToAdd);
    QCOMPARE(testZip.getComment(), globalComment);
    testZip.close();
    removeTestFiles(fileNames);
    removeTestFiles(fileNamesToAdd);
    curDir.remove(zipName);
}
<|endoftext|>


<commit_before>#include <type_traits>

#if 1
#include <archie/utils/fused/tuple.h>
namespace fused = archie::utils::fused;
using fused::make_tuple;
using fused::get;
using fused::tuple;
using fused::tuple_size;
namespace test = archie::utils::fused;
#else
#include <tuple>
using std::make_tuple;
using std::get;
using std::tuple;
using std::tuple_size;
namespace test = std;
#endif

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

namespace {

struct tuple_test : ::testing::Test {};

TEST_F(tuple_test, canDefaultConstruct) {
  static_assert(
      std::is_default_constructible<tuple<unsigned, double, char>>::value, "");
  auto t = tuple<unsigned, double, char>();

  EXPECT_EQ(3u, tuple_size<decltype(t)>::value);
  EXPECT_LE(sizeof(unsigned) + sizeof(double) + sizeof(char), sizeof(t));
}

TEST_F(tuple_test, canDefaultConstructTupleWithUncopyableElement) {
  static_assert(std::is_default_constructible<
                    tuple<unsigned, std::unique_ptr<double>, char>>::value,
                "");

  auto t = tuple<unsigned, std::unique_ptr<double>, char>{};

  EXPECT_EQ(3u, tuple_size<decltype(t)>::value);
  EXPECT_LE(sizeof(unsigned) + sizeof(std::unique_ptr<double>) + sizeof(char),
            sizeof(t));
}

TEST_F(tuple_test, canConstruct) {
  static_assert(std::is_constructible<tuple<unsigned, double, char>, unsigned,
                                      double, char>::value,
                "");
  auto t = tuple<unsigned, double, char>(1u, 2.0, '3');

  EXPECT_EQ(3u, tuple_size<decltype(t)>::value);
  EXPECT_LE(sizeof(unsigned) + sizeof(double) + sizeof(char), sizeof(t));
}

TEST_F(tuple_test, canConstructTupleWithUncopyableElement) {
  static_assert(std::is_constructible <
                    tuple<unsigned, std::unique_ptr<double>, char>,
                unsigned, std::unique_ptr<double>&&, char > ::value, "");
  auto t = tuple<unsigned, std::unique_ptr<double>, char>(
      1u, std::make_unique<double>(2.0), '3');

  EXPECT_EQ(3u, tuple_size<decltype(t)>::value);
  EXPECT_LE(sizeof(unsigned) + sizeof(std::unique_ptr<double>) + sizeof(char),
            sizeof(t));
}

TEST_F(tuple_test, makeTupleTakesElementsByValue) {
  unsigned a = 1u;
  double b = 2.0;
  char c = '3';

  auto t = test::make_tuple(a, b, c);

  ASSERT_EQ(3u, tuple_size<decltype(t)>::value);

  EXPECT_EQ(a, get<0>(t));
  EXPECT_EQ(c, get<2>(t));

  EXPECT_NE(&a, &get<0>(t));
  EXPECT_NE(&b, &get<1>(t));
  EXPECT_NE(&c, &get<2>(t));
}

TEST_F(tuple_test, makeTupleTakesElementsByRValue) {
  auto ptr = std::make_unique<char>('3');
  auto t = test::make_tuple(1u, std::make_unique<double>(2.0), std::move(ptr));

  ASSERT_EQ(3u, tuple_size<decltype(t)>::value);

  EXPECT_EQ(2.0, *get<1>(t));
  EXPECT_EQ('3', *get<2>(t));
}

TEST_F(tuple_test, canUseGetByIdToRead) {
  auto t = test::make_tuple(1u, 2.0, '3');

  ASSERT_EQ(3u, tuple_size<decltype(t)>::value);

  EXPECT_EQ(1u, get<0>(t));
  EXPECT_EQ(2.0, get<1>(t));
  EXPECT_EQ('3', get<2>(t));

  auto const& x = get<0>(t);
  EXPECT_EQ(1u, x);
  auto const& y = get<0>(t);
  EXPECT_EQ(&x, &y);
}

TEST_F(tuple_test, canUseGetByIdToWrite) {
  auto t = test::make_tuple(1u, std::make_unique<double>(2.0), '3');

  ASSERT_EQ(3u, tuple_size<decltype(t)>::value);

  auto const& x = get<0>(t);
  ASSERT_EQ(1u, x);
  auto& y = get<0>(t);
  ASSERT_EQ(&x, &y);
  y = 4u;
  EXPECT_EQ(4u, x);
  get<0>(t) = 5u;
  EXPECT_EQ(5u, x);

  ASSERT_EQ(2.0, *get<1>(t));
  get<1>(t) = std::move(std::make_unique<double>(3.0));
  EXPECT_EQ(3.0, *get<1>(t));
}

TEST_F(tuple_test, canCopyConstruct) {
  static_assert(!std::is_copy_constructible<
                    tuple<unsigned, std::unique_ptr<double>, char>>::value,
                "");
  static_assert(
      std::is_copy_constructible<tuple<unsigned, double, char>>::value, "");
  auto orig = tuple<unsigned, double, char>(1u, 2.0, '3');
  auto copy = orig;

  ASSERT_EQ(3u, tuple_size<decltype(orig)>::value);
  ASSERT_EQ(3u, tuple_size<decltype(copy)>::value);

  EXPECT_EQ(get<0>(orig), get<0>(copy));
  EXPECT_EQ(get<1>(orig), get<1>(copy));
  EXPECT_EQ(get<2>(orig), get<2>(copy));

  EXPECT_NE(&get<0>(orig), &get<0>(copy));
  EXPECT_NE(&get<1>(orig), &get<1>(copy));
  EXPECT_NE(&get<2>(orig), &get<2>(copy));
}

TEST_F(tuple_test, canCopyAssign) {
  static_assert(std::is_copy_assignable<tuple<unsigned, double, char>>::value,
                "");
  auto orig = tuple<unsigned, double, char>(1u, 2.0, '3');
  auto copy = tuple<unsigned, double, char>(2u, 4.0, '6');

  ASSERT_EQ(3u, tuple_size<decltype(orig)>::value);
  ASSERT_EQ(3u, tuple_size<decltype(copy)>::value);

  ASSERT_EQ(2u, get<0>(copy));
  ASSERT_EQ(4.0, get<1>(copy));
  ASSERT_EQ('6', get<2>(copy));

  copy = orig;

  ASSERT_EQ(3u, tuple_size<decltype(copy)>::value);

  EXPECT_EQ(1u, get<0>(copy));
  EXPECT_EQ(2.0, get<1>(copy));
  EXPECT_EQ('3', get<2>(copy));
}

TEST_F(tuple_test, canMoveConstruct) {
  static_assert(
      std::is_move_constructible<tuple<unsigned, double, char>>::value, "");
  auto orig = tuple<unsigned, double, char>(1u, 2.0, '3');

  ASSERT_EQ(3u, tuple_size<decltype(orig)>::value);

  ASSERT_EQ(1u, get<0>(orig));
  ASSERT_EQ(2.0, get<1>(orig));
  ASSERT_EQ('3', get<2>(orig));

  auto copy = tuple<unsigned, double, char>{std::move(orig)};

  ASSERT_EQ(3u, tuple_size<decltype(copy)>::value);

  EXPECT_EQ(1u, get<0>(copy));
  EXPECT_EQ(2.0, get<1>(copy));
  EXPECT_EQ('3', get<2>(copy));
}

TEST_F(tuple_test, canMoveAssign) {
  static_assert(std::is_move_assignable<tuple<unsigned, double, char>>::value,
                "");
  auto orig = tuple<unsigned, double, char>(1u, 2.0, '3');
  auto copy = tuple<unsigned, double, char>(2u, 4.0, '6');

  ASSERT_EQ(3u, tuple_size<decltype(orig)>::value);
  ASSERT_EQ(3u, tuple_size<decltype(copy)>::value);

  ASSERT_EQ(2u, get<0>(copy));
  ASSERT_EQ(4.0, get<1>(copy));
  ASSERT_EQ('6', get<2>(copy));

  copy = std::move(orig);

  ASSERT_EQ(3u, tuple_size<decltype(copy)>::value);

  EXPECT_EQ(1u, get<0>(copy));
  EXPECT_EQ(2.0, get<1>(copy));
  EXPECT_EQ('3', get<2>(copy));
}

TEST_F(tuple_test, canCompareEquality) {
  auto t1 = make_tuple(1u, 2.0, '3');
  auto t2 = make_tuple(1u, 2.0, '3');
  auto t3 = make_tuple(0u, 2.0, '3');
  auto t4 = make_tuple(1u, 2.0, '4');

  EXPECT_TRUE(t1 == t1);
  EXPECT_TRUE(t1 == t2);
  EXPECT_FALSE(t1 == t3);
  EXPECT_FALSE(t1 == t4);
}

TEST_F(tuple_test, canCompareInequality) {
  auto t1 = make_tuple(1u, 2.0, '3');
  auto t2 = make_tuple(1u, 2.0, '3');
  auto t3 = make_tuple(0u, 2.0, '3');
  auto t4 = make_tuple(1u, 2.0, '4');

  EXPECT_FALSE(t1 != t1);
  EXPECT_FALSE(t1 != t2);
  EXPECT_TRUE(t1 != t3);
  EXPECT_TRUE(t1 != t4);
}

TEST_F(tuple_test, canLexicographicalCompareLess) {
  auto t1 = make_tuple(1u, 2.0, '3');
  auto t2 = make_tuple(1u, 2.0, '3');
  auto t3 = make_tuple(0u, 2.0, '3');
  auto t4 = make_tuple(1u, 2.0, '4');
  auto t5 = make_tuple(2u, 2.0, '1');

  EXPECT_FALSE(t1 < t1);
  EXPECT_FALSE(t1 < t2);
  EXPECT_FALSE(t2 < t1);

  EXPECT_FALSE(t1 < t3);
  EXPECT_TRUE(t3 < t1);

  EXPECT_TRUE(t1 < t4);
  EXPECT_FALSE(t4 < t1);

  EXPECT_TRUE(t1 < t5);
  EXPECT_FALSE(t5 < t1);
}

TEST_F(tuple_test, canAssignSimilarTuples) {
  auto t = test::make_tuple(1ul, 2ul, 3ul);
  t = test::make_tuple(4, 5, 6);

  EXPECT_EQ(4u, test::get<0>(t));
  EXPECT_EQ(5u, test::get<1>(t));
  EXPECT_EQ(6u, test::get<2>(t));
}

TEST_F(tuple_test, canStoreReferencesInTuple) {
  unsigned a = 1u;
  double b = 2.0;
  char c = '3';

  auto t = test::tuple<unsigned&, double&, char&>(a, b, c);

  EXPECT_EQ(&a, &test::get<0>(t));
  EXPECT_EQ(&b, &test::get<1>(t));
  EXPECT_EQ(&c, &test::get<2>(t));

  test::get<0>(t) = 4u;
  test::get<1>(t) = 5.0;
  test::get<2>(t) = '6';

  EXPECT_EQ(4u, a);
  EXPECT_EQ(5.0, b);
  EXPECT_EQ('6', c);

  t = test::make_tuple(7u, 8.0, '9');

  EXPECT_EQ(7u, a);
  EXPECT_EQ(8.0, b);
  EXPECT_EQ('9', c);
}
}
<commit_msg>can store values in tuple test<commit_after>#include <type_traits>

#if 1
#include <archie/utils/fused/tuple.h>
namespace test = archie::utils::fused;
#else
#include <tuple>
namespace test = std;
#endif

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

namespace {

struct tuple_test : ::testing::Test {};

TEST_F(tuple_test, canDefaultConstruct) {
  static_assert(
      std::is_default_constructible<test::tuple<unsigned, double, char>>::value,
      "");
  auto t = test::tuple<unsigned, double, char>();

  EXPECT_EQ(3u, test::tuple_size<decltype(t)>::value);
  EXPECT_LE(sizeof(unsigned) + sizeof(double) + sizeof(char), sizeof(t));
}

TEST_F(tuple_test, canDefaultConstructTupleWithUncopyableElement) {
  static_assert(
      std::is_default_constructible<
          test::tuple<unsigned, std::unique_ptr<double>, char>>::value,
      "");

  auto t = test::tuple<unsigned, std::unique_ptr<double>, char>{};

  EXPECT_EQ(3u, test::tuple_size<decltype(t)>::value);
  EXPECT_LE(sizeof(unsigned) + sizeof(std::unique_ptr<double>) + sizeof(char),
            sizeof(t));
}

TEST_F(tuple_test, canConstruct) {
  static_assert(std::is_constructible<test::tuple<unsigned, double, char>,
                                      unsigned, double, char>::value,
                "");
  auto t = test::tuple<unsigned, double, char>(1u, 2.0, '3');

  EXPECT_EQ(3u, test::tuple_size<decltype(t)>::value);
  EXPECT_LE(sizeof(unsigned) + sizeof(double) + sizeof(char), sizeof(t));
}

TEST_F(tuple_test, canConstructTupleWithUncopyableElement) {
  static_assert(std::is_constructible <
                    test::tuple<unsigned, std::unique_ptr<double>, char>,
                unsigned, std::unique_ptr<double>&&, char > ::value, "");
  auto t = test::tuple<unsigned, std::unique_ptr<double>, char>(
      1u, std::make_unique<double>(2.0), '3');

  EXPECT_EQ(3u, test::tuple_size<decltype(t)>::value);
  EXPECT_LE(sizeof(unsigned) + sizeof(std::unique_ptr<double>) + sizeof(char),
            sizeof(t));
}

TEST_F(tuple_test, makeTupleTakesElementsByValue) {
  unsigned a = 1u;
  double b = 2.0;
  char c = '3';

  auto t = test::make_tuple(a, b, c);

  ASSERT_EQ(3u, test::tuple_size<decltype(t)>::value);

  EXPECT_EQ(a, test::get<0>(t));
  EXPECT_EQ(b, test::get<1>(t));
  EXPECT_EQ(c, test::get<2>(t));

  EXPECT_NE(&a, &test::get<0>(t));
  EXPECT_NE(&b, &test::get<1>(t));
  EXPECT_NE(&c, &test::get<2>(t));
}

TEST_F(tuple_test, makeTupleTakesElementsByRValue) {
  auto ptr = std::make_unique<char>('3');
  auto t = test::make_tuple(1u, std::make_unique<double>(2.0), std::move(ptr));

  ASSERT_EQ(3u, test::tuple_size<decltype(t)>::value);

  EXPECT_EQ(2.0, *test::get<1>(t));
  EXPECT_EQ('3', *test::get<2>(t));
}

TEST_F(tuple_test, canUseGetByIdToRead) {
  auto t = test::make_tuple(1u, 2.0, '3');

  ASSERT_EQ(3u, test::tuple_size<decltype(t)>::value);

  EXPECT_EQ(1u, test::get<0>(t));
  EXPECT_EQ(2.0, test::get<1>(t));
  EXPECT_EQ('3', test::get<2>(t));

  auto const& x = test::get<0>(t);
  EXPECT_EQ(1u, x);
  auto const& y = test::get<0>(t);
  EXPECT_EQ(&x, &y);
}

TEST_F(tuple_test, canUseGetByIdToWrite) {
  auto t = test::make_tuple(1u, std::make_unique<double>(2.0), '3');

  ASSERT_EQ(3u, test::tuple_size<decltype(t)>::value);

  auto const& x = test::get<0>(t);
  ASSERT_EQ(1u, x);
  auto& y = test::get<0>(t);
  ASSERT_EQ(&x, &y);
  y = 4u;
  EXPECT_EQ(4u, x);
  test::get<0>(t) = 5u;
  EXPECT_EQ(5u, x);

  ASSERT_EQ(2.0, *test::get<1>(t));
  test::get<1>(t) = std::move(std::make_unique<double>(3.0));
  EXPECT_EQ(3.0, *test::get<1>(t));
}

TEST_F(tuple_test, canCopyConstruct) {
  static_assert(
      !std::is_copy_constructible<
          test::tuple<unsigned, std::unique_ptr<double>, char>>::value,
      "");
  static_assert(
      std::is_copy_constructible<test::tuple<unsigned, double, char>>::value,
      "");
  auto orig = test::tuple<unsigned, double, char>(1u, 2.0, '3');
  auto copy = orig;

  ASSERT_EQ(3u, test::tuple_size<decltype(orig)>::value);
  ASSERT_EQ(3u, test::tuple_size<decltype(copy)>::value);

  EXPECT_EQ(test::get<0>(orig), test::get<0>(copy));
  EXPECT_EQ(test::get<1>(orig), test::get<1>(copy));
  EXPECT_EQ(test::get<2>(orig), test::get<2>(copy));

  EXPECT_NE(&test::get<0>(orig), &test::get<0>(copy));
  EXPECT_NE(&test::get<1>(orig), &test::get<1>(copy));
  EXPECT_NE(&test::get<2>(orig), &test::get<2>(copy));
}

TEST_F(tuple_test, canCopyAssign) {
  static_assert(
      std::is_copy_assignable<test::tuple<unsigned, double, char>>::value, "");
  auto orig = test::tuple<unsigned, double, char>(1u, 2.0, '3');
  auto copy = test::tuple<unsigned, double, char>(2u, 4.0, '6');

  ASSERT_EQ(3u, test::tuple_size<decltype(orig)>::value);
  ASSERT_EQ(3u, test::tuple_size<decltype(copy)>::value);

  ASSERT_EQ(2u, test::get<0>(copy));
  ASSERT_EQ(4.0, test::get<1>(copy));
  ASSERT_EQ('6', test::get<2>(copy));

  copy = orig;

  ASSERT_EQ(3u, test::tuple_size<decltype(copy)>::value);

  EXPECT_EQ(1u, test::get<0>(copy));
  EXPECT_EQ(2.0, test::get<1>(copy));
  EXPECT_EQ('3', test::get<2>(copy));
}

TEST_F(tuple_test, canMoveConstruct) {
  static_assert(
      std::is_move_constructible<test::tuple<unsigned, double, char>>::value,
      "");
  auto orig = test::tuple<unsigned, double, char>(1u, 2.0, '3');

  ASSERT_EQ(3u, test::tuple_size<decltype(orig)>::value);

  ASSERT_EQ(1u, test::get<0>(orig));
  ASSERT_EQ(2.0, test::get<1>(orig));
  ASSERT_EQ('3', test::get<2>(orig));

  auto copy = test::tuple<unsigned, double, char>{std::move(orig)};

  ASSERT_EQ(3u, test::tuple_size<decltype(copy)>::value);

  EXPECT_EQ(1u, test::get<0>(copy));
  EXPECT_EQ(2.0, test::get<1>(copy));
  EXPECT_EQ('3', test::get<2>(copy));
}

TEST_F(tuple_test, canMoveAssign) {
  static_assert(
      std::is_move_assignable<test::tuple<unsigned, double, char>>::value, "");
  auto orig = test::tuple<unsigned, double, char>(1u, 2.0, '3');
  auto copy = test::tuple<unsigned, double, char>(2u, 4.0, '6');

  ASSERT_EQ(3u, test::tuple_size<decltype(orig)>::value);
  ASSERT_EQ(3u, test::tuple_size<decltype(copy)>::value);

  ASSERT_EQ(2u, test::get<0>(copy));
  ASSERT_EQ(4.0, test::get<1>(copy));
  ASSERT_EQ('6', test::get<2>(copy));

  copy = std::move(orig);

  ASSERT_EQ(3u, test::tuple_size<decltype(copy)>::value);

  EXPECT_EQ(1u, test::get<0>(copy));
  EXPECT_EQ(2.0, test::get<1>(copy));
  EXPECT_EQ('3', test::get<2>(copy));
}

TEST_F(tuple_test, canCompareEquality) {
  auto t1 = test::make_tuple(1u, 2.0, '3');
  auto t2 = test::make_tuple(1u, 2.0, '3');
  auto t3 = test::make_tuple(0u, 2.0, '3');
  auto t4 = test::make_tuple(1u, 2.0, '4');

  EXPECT_TRUE(t1 == t1);
  EXPECT_TRUE(t1 == t2);
  EXPECT_FALSE(t1 == t3);
  EXPECT_FALSE(t1 == t4);
}

TEST_F(tuple_test, canCompareInequality) {
  auto t1 = test::make_tuple(1u, 2.0, '3');
  auto t2 = test::make_tuple(1u, 2.0, '3');
  auto t3 = test::make_tuple(0u, 2.0, '3');
  auto t4 = test::make_tuple(1u, 2.0, '4');

  EXPECT_FALSE(t1 != t1);
  EXPECT_FALSE(t1 != t2);
  EXPECT_TRUE(t1 != t3);
  EXPECT_TRUE(t1 != t4);
}

TEST_F(tuple_test, canLexicographicalCompareLess) {
  auto t1 = test::make_tuple(1u, 2.0, '3');
  auto t2 = test::make_tuple(1u, 2.0, '3');
  auto t3 = test::make_tuple(0u, 2.0, '3');
  auto t4 = test::make_tuple(1u, 2.0, '4');
  auto t5 = test::make_tuple(2u, 2.0, '1');

  EXPECT_FALSE(t1 < t1);
  EXPECT_FALSE(t1 < t2);
  EXPECT_FALSE(t2 < t1);

  EXPECT_FALSE(t1 < t3);
  EXPECT_TRUE(t3 < t1);

  EXPECT_TRUE(t1 < t4);
  EXPECT_FALSE(t4 < t1);

  EXPECT_TRUE(t1 < t5);
  EXPECT_FALSE(t5 < t1);
}

TEST_F(tuple_test, canAssignSimilarTuples) {
  auto t = test::make_tuple(1ul, 2ul, 3ul);
  t = test::make_tuple(4, 5, 6);

  EXPECT_EQ(4u, test::get<0>(t));
  EXPECT_EQ(5u, test::get<1>(t));
  EXPECT_EQ(6u, test::get<2>(t));
}

TEST_F(tuple_test, canStoreValuesInTuple) {
  unsigned a = 1u;
  double b = 2.0;
  char c = '3';

  auto t = test::tuple<unsigned, double, char>(a, b, c);

  EXPECT_NE(&a, &test::get<0>(t));
  EXPECT_NE(&b, &test::get<1>(t));
  EXPECT_NE(&c, &test::get<2>(t));

  test::get<0>(t) = 0;
  test::get<1>(t) = 0;
  test::get<2>(t) = 0;

  ASSERT_EQ(0u, test::get<0>(t));
  ASSERT_EQ(0, test::get<1>(t));
  ASSERT_EQ(0, test::get<2>(t));

  EXPECT_EQ(1u, a);
  EXPECT_EQ(2.0, b);
  EXPECT_EQ('3', c);
}

TEST_F(tuple_test, canStoreReferencesInTuple) {
  unsigned a = 1u;
  double b = 2.0;
  char c = '3';

  auto t = test::tuple<unsigned&, double&, char&>(a, b, c);

  EXPECT_EQ(&a, &test::get<0>(t));
  EXPECT_EQ(&b, &test::get<1>(t));
  EXPECT_EQ(&c, &test::get<2>(t));

  test::get<0>(t) = 4u;
  test::get<1>(t) = 5.0;
  test::get<2>(t) = '6';

  EXPECT_EQ(4u, a);
  EXPECT_EQ(5.0, b);
  EXPECT_EQ('6', c);

  t = test::make_tuple(7u, 8.0, '9');

  EXPECT_EQ(7u, a);
  EXPECT_EQ(8.0, b);
  EXPECT_EQ('9', c);
}
}
<|endoftext|>


<commit_before>/*
 * Copyright 2016 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

// This is a GPU-backend specific test.

#include "Test.h"

#if SK_SUPPORT_GPU
#include "GrSurfaceProxy.h"
#include "GrTextureProxy.h"
#include "GrRenderTargetPriv.h"
#include "GrRenderTargetProxy.h"

// Check that the surface proxy's member vars are set as expected
static void check_surface(skiatest::Reporter* reporter,
                          GrSurfaceProxy* proxy,
                          GrSurfaceOrigin origin,
                          int width, int height,
                          GrPixelConfig config,
                          const GrGpuResource::UniqueID& uniqueID,
                          SkBudgeted budgeted) {
    REPORTER_ASSERT(reporter, proxy->origin() == origin);
    REPORTER_ASSERT(reporter, proxy->width() == width);
    REPORTER_ASSERT(reporter, proxy->height() == height);
    REPORTER_ASSERT(reporter, proxy->config() == config);
    if (!uniqueID.isInvalid()) {
        REPORTER_ASSERT(reporter, proxy->uniqueID().asUInt() == uniqueID.asUInt());
    } else {
        REPORTER_ASSERT(reporter, !proxy->uniqueID().isInvalid());
    }
    REPORTER_ASSERT(reporter, proxy->isBudgeted() == budgeted);
}

static void check_rendertarget(skiatest::Reporter* reporter,
                               const GrCaps& caps,
                               GrTextureProvider* provider,
                               GrRenderTargetProxy* rtProxy,
                               int numSamples,
                               SkBackingFit fit,
                               int expectedMaxWindowRects,
                               bool wasWrapped) {
    REPORTER_ASSERT(reporter, rtProxy->maxWindowRectangles(caps) == expectedMaxWindowRects);
    REPORTER_ASSERT(reporter, rtProxy->numStencilSamples() == numSamples);

    GrSurfaceProxy::UniqueID idBefore = rtProxy->uniqueID();
    GrRenderTarget* rt = rtProxy->instantiate(provider);
    REPORTER_ASSERT(reporter, rt);

    REPORTER_ASSERT(reporter, rtProxy->uniqueID() == idBefore);
    if (wasWrapped) {
        // Wrapped resources share their uniqueID with the wrapping RenderTargetProxy
        REPORTER_ASSERT(reporter, rtProxy->uniqueID().asUInt() == rt->uniqueID().asUInt());
    } else {
        // Deferred resources should always have a different ID from their instantiated rendertarget
        REPORTER_ASSERT(reporter, rtProxy->uniqueID().asUInt() != rt->uniqueID().asUInt());
    }

    REPORTER_ASSERT(reporter, rt->origin() == rtProxy->origin());
    if (SkBackingFit::kExact == fit) {
        REPORTER_ASSERT(reporter, rt->width() == rtProxy->width());
        REPORTER_ASSERT(reporter, rt->height() == rtProxy->height());
    } else {
        REPORTER_ASSERT(reporter, rt->width() >= rtProxy->width());
        REPORTER_ASSERT(reporter, rt->height() >= rtProxy->height());
    }
    REPORTER_ASSERT(reporter, rt->config() == rtProxy->config());

    REPORTER_ASSERT(reporter, rt->isUnifiedMultisampled() == rtProxy->isUnifiedMultisampled());
    REPORTER_ASSERT(reporter, rt->isStencilBufferMultisampled() ==
                              rtProxy->isStencilBufferMultisampled());
    REPORTER_ASSERT(reporter, rt->numColorSamples() == rtProxy->numColorSamples());
    REPORTER_ASSERT(reporter, rt->numStencilSamples() == rtProxy->numStencilSamples());
    REPORTER_ASSERT(reporter, rt->isMixedSampled() == rtProxy->isMixedSampled());
    REPORTER_ASSERT(reporter, rt->renderTargetPriv().flags() == rtProxy->testingOnly_getFlags());
}

static void check_texture(skiatest::Reporter* reporter,
                          GrTextureProvider* provider,
                          GrTextureProxy* texProxy,
                          SkBackingFit fit,
                          bool wasWrapped) {
    GrSurfaceProxy::UniqueID idBefore = texProxy->uniqueID();
    GrTexture* tex = texProxy->instantiate(provider);
    REPORTER_ASSERT(reporter, tex);

    REPORTER_ASSERT(reporter, texProxy->uniqueID() == idBefore);
    if (wasWrapped) {
        // Wrapped resources share their uniqueID with the wrapping TextureProxy
        REPORTER_ASSERT(reporter, texProxy->uniqueID().asUInt() == tex->uniqueID().asUInt());
    } else {
        // Deferred resources should always have a different ID from their instantiated texture
        REPORTER_ASSERT(reporter, texProxy->uniqueID().asUInt() != tex->uniqueID().asUInt());
    }

    REPORTER_ASSERT(reporter, tex->origin() == texProxy->origin());
    if (SkBackingFit::kExact == fit) {
        REPORTER_ASSERT(reporter, tex->width() == texProxy->width());
        REPORTER_ASSERT(reporter, tex->height() == texProxy->height());
    } else {
        REPORTER_ASSERT(reporter, tex->width() >= texProxy->width());
        REPORTER_ASSERT(reporter, tex->height() >= texProxy->height());
    }
    REPORTER_ASSERT(reporter, tex->config() == texProxy->config());
}

DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DeferredProxyTest, reporter, ctxInfo) {
    GrTextureProvider* provider = ctxInfo.grContext()->textureProvider();
    const GrCaps& caps = *ctxInfo.grContext()->caps();

    const GrGpuResource::UniqueID kInvalidResourceID = GrGpuResource::UniqueID::InvalidID();

    for (auto origin : { kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin }) {
        for (auto widthHeight : { 100, 128 }) {
            for (auto config : { kAlpha_8_GrPixelConfig, kRGBA_8888_GrPixelConfig }) {
                for (auto fit : { SkBackingFit::kExact, SkBackingFit::kApprox }) {
                    for (auto budgeted : { SkBudgeted::kYes, SkBudgeted::kNo }) {
                        for (auto numSamples : { 0, 4}) {
                            bool renderable = ctxInfo.grContext()->caps()->isConfigRenderable(
                                                                      config, numSamples > 0) &&
                                  numSamples <= ctxInfo.grContext()->caps()->maxColorSampleCount();

                            GrSurfaceDesc desc;
                            desc.fFlags = kRenderTarget_GrSurfaceFlag;
                            desc.fOrigin = origin;
                            desc.fWidth = widthHeight;
                            desc.fHeight = widthHeight;
                            desc.fConfig = config;
                            desc.fSampleCnt = numSamples;

                            if (renderable) {
                                sk_sp<GrSurfaceProxy> sProxy(GrSurfaceProxy::MakeDeferred(
                                                                                caps, desc,
                                                                                fit, budgeted));
                                // This forces the proxy to compute and cache its pre-instantiation
                                // size guess. Later, when it is actually instantiated, it checks
                                // that the instantiated size is <= to the pre-computation.
                                // If the proxy never computed its pre-instantiation size then the
                                // check is skipped.
                                sProxy->gpuMemorySize();

                                check_surface(reporter, sProxy.get(), origin,
                                              widthHeight, widthHeight, config,
                                              kInvalidResourceID, budgeted);
                                check_rendertarget(reporter, caps, provider,
                                                   sProxy->asRenderTargetProxy(), numSamples,
                                                   fit, caps.maxWindowRectangles(), false);
                            }

                            desc.fFlags = kNone_GrSurfaceFlags;
                            desc.fSampleCnt = 0;

                            sk_sp<GrSurfaceProxy> sProxy(GrSurfaceProxy::MakeDeferred(caps,
                                                                                desc,
                                                                                fit,
                                                                                budgeted));
                            // This forces the proxy to compute and cache its pre-instantiation
                            // size guess. Later, when it is actually instantiated, it checks
                            // that the instantiated size is <= to the pre-computation.
                            // If the proxy never computed its pre-instantiation size then the
                            // check is skipped.
                            sProxy->gpuMemorySize();

                            check_surface(reporter, sProxy.get(), origin,
                                          widthHeight, widthHeight, config,
                                          kInvalidResourceID, budgeted);
                            check_texture(reporter, provider, sProxy->asTextureProxy(), fit, false);
                        }
                    }
                }
            }
        }
    }
}

DEF_GPUTEST_FOR_RENDERING_CONTEXTS(WrappedProxyTest, reporter, ctxInfo) {
    GrTextureProvider* provider = ctxInfo.grContext()->textureProvider();
    const GrCaps& caps = *ctxInfo.grContext()->caps();

    static const int kWidthHeight = 100;

    for (auto origin : { kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin }) {
        for (auto config : { kAlpha_8_GrPixelConfig, kRGBA_8888_GrPixelConfig }) {
            for (auto budgeted : { SkBudgeted::kYes, SkBudgeted::kNo }) {
                for (auto numSamples: { 0, 4}) {
                    bool renderable = caps.isConfigRenderable(config, numSamples > 0);

                    GrSurfaceDesc desc;
                    desc.fOrigin = origin;
                    desc.fWidth = kWidthHeight;
                    desc.fHeight = kWidthHeight;
                    desc.fConfig = config;
                    desc.fSampleCnt = numSamples;

                    // External on-screen render target.
                    if (renderable && kOpenGL_GrBackend == ctxInfo.backend()) {
                        GrBackendRenderTargetDesc backendDesc;
                        backendDesc.fWidth = kWidthHeight;
                        backendDesc.fHeight = kWidthHeight;
                        backendDesc.fConfig = config;
                        backendDesc.fOrigin = origin;
                        backendDesc.fSampleCnt = numSamples;
                        backendDesc.fStencilBits = 8;
                        backendDesc.fRenderTargetHandle = 0;

                        sk_sp<GrRenderTarget> defaultFBO(
                            provider->wrapBackendRenderTarget(backendDesc));

                        sk_sp<GrSurfaceProxy> sProxy(GrSurfaceProxy::MakeWrapped(defaultFBO));
                        check_surface(reporter, sProxy.get(), origin,
                                      kWidthHeight, kWidthHeight, config,
                                      defaultFBO->uniqueID(), SkBudgeted::kNo);
                        check_rendertarget(reporter, caps, provider, sProxy->asRenderTargetProxy(),
                                           numSamples, SkBackingFit::kExact, 0, true);
                    }

                    sk_sp<GrTexture> tex;

                    // Internal offscreen render target.
                    if (renderable) {
                        desc.fFlags = kRenderTarget_GrSurfaceFlag;
                        tex.reset(provider->createTexture(desc, budgeted));
                        sk_sp<GrRenderTarget> rt(sk_ref_sp(tex->asRenderTarget()));

                        sk_sp<GrSurfaceProxy> sProxy(GrSurfaceProxy::MakeWrapped(rt));
                        check_surface(reporter, sProxy.get(), origin,
                                      kWidthHeight, kWidthHeight, config,
                                      rt->uniqueID(), budgeted);
                        check_rendertarget(reporter, caps, provider, sProxy->asRenderTargetProxy(),
                                           numSamples, SkBackingFit::kExact,
                                           caps.maxWindowRectangles(), true);
                    }

                    if (!tex) {
                        SkASSERT(kNone_GrSurfaceFlags == desc.fFlags );
                        desc.fSampleCnt = 0;
                        tex.reset(provider->createTexture(desc, budgeted));
                    }

                    sk_sp<GrSurfaceProxy> sProxy(GrSurfaceProxy::MakeWrapped(tex));
                    check_surface(reporter, sProxy.get(), origin,
                                  kWidthHeight, kWidthHeight, config, tex->uniqueID(), budgeted);
                    check_texture(reporter, provider, sProxy->asTextureProxy(),
                                  SkBackingFit::kExact, true);
                }
            }
        }
    }
}

#endif
<commit_msg>Add test for outre GrSurfaceProxy requests<commit_after>/*
 * Copyright 2016 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

// This is a GPU-backend specific test.

#include "Test.h"

#if SK_SUPPORT_GPU
#include "GrSurfaceProxy.h"
#include "GrTextureProxy.h"
#include "GrRenderTargetPriv.h"
#include "GrRenderTargetProxy.h"

// Check that the surface proxy's member vars are set as expected
static void check_surface(skiatest::Reporter* reporter,
                          GrSurfaceProxy* proxy,
                          GrSurfaceOrigin origin,
                          int width, int height,
                          GrPixelConfig config,
                          const GrGpuResource::UniqueID& uniqueID,
                          SkBudgeted budgeted) {
    REPORTER_ASSERT(reporter, proxy->origin() == origin);
    REPORTER_ASSERT(reporter, proxy->width() == width);
    REPORTER_ASSERT(reporter, proxy->height() == height);
    REPORTER_ASSERT(reporter, proxy->config() == config);
    if (!uniqueID.isInvalid()) {
        REPORTER_ASSERT(reporter, proxy->uniqueID().asUInt() == uniqueID.asUInt());
    } else {
        REPORTER_ASSERT(reporter, !proxy->uniqueID().isInvalid());
    }
    REPORTER_ASSERT(reporter, proxy->isBudgeted() == budgeted);
}

static void check_rendertarget(skiatest::Reporter* reporter,
                               const GrCaps& caps,
                               GrTextureProvider* provider,
                               GrRenderTargetProxy* rtProxy,
                               int numSamples,
                               SkBackingFit fit,
                               int expectedMaxWindowRects,
                               bool wasWrapped) {
    REPORTER_ASSERT(reporter, rtProxy->maxWindowRectangles(caps) == expectedMaxWindowRects);
    REPORTER_ASSERT(reporter, rtProxy->numStencilSamples() == numSamples);

    GrSurfaceProxy::UniqueID idBefore = rtProxy->uniqueID();
    GrRenderTarget* rt = rtProxy->instantiate(provider);
    REPORTER_ASSERT(reporter, rt);

    REPORTER_ASSERT(reporter, rtProxy->uniqueID() == idBefore);
    if (wasWrapped) {
        // Wrapped resources share their uniqueID with the wrapping RenderTargetProxy
        REPORTER_ASSERT(reporter, rtProxy->uniqueID().asUInt() == rt->uniqueID().asUInt());
    } else {
        // Deferred resources should always have a different ID from their instantiated rendertarget
        REPORTER_ASSERT(reporter, rtProxy->uniqueID().asUInt() != rt->uniqueID().asUInt());
    }

    REPORTER_ASSERT(reporter, rt->origin() == rtProxy->origin());
    if (SkBackingFit::kExact == fit) {
        REPORTER_ASSERT(reporter, rt->width() == rtProxy->width());
        REPORTER_ASSERT(reporter, rt->height() == rtProxy->height());
    } else {
        REPORTER_ASSERT(reporter, rt->width() >= rtProxy->width());
        REPORTER_ASSERT(reporter, rt->height() >= rtProxy->height());
    }
    REPORTER_ASSERT(reporter, rt->config() == rtProxy->config());

    REPORTER_ASSERT(reporter, rt->isUnifiedMultisampled() == rtProxy->isUnifiedMultisampled());
    REPORTER_ASSERT(reporter, rt->isStencilBufferMultisampled() ==
                              rtProxy->isStencilBufferMultisampled());
    REPORTER_ASSERT(reporter, rt->numColorSamples() == rtProxy->numColorSamples());
    REPORTER_ASSERT(reporter, rt->numStencilSamples() == rtProxy->numStencilSamples());
    REPORTER_ASSERT(reporter, rt->isMixedSampled() == rtProxy->isMixedSampled());
    REPORTER_ASSERT(reporter, rt->renderTargetPriv().flags() == rtProxy->testingOnly_getFlags());
}

static void check_texture(skiatest::Reporter* reporter,
                          GrTextureProvider* provider,
                          GrTextureProxy* texProxy,
                          SkBackingFit fit,
                          bool wasWrapped) {
    GrSurfaceProxy::UniqueID idBefore = texProxy->uniqueID();
    GrTexture* tex = texProxy->instantiate(provider);
    REPORTER_ASSERT(reporter, tex);

    REPORTER_ASSERT(reporter, texProxy->uniqueID() == idBefore);
    if (wasWrapped) {
        // Wrapped resources share their uniqueID with the wrapping TextureProxy
        REPORTER_ASSERT(reporter, texProxy->uniqueID().asUInt() == tex->uniqueID().asUInt());
    } else {
        // Deferred resources should always have a different ID from their instantiated texture
        REPORTER_ASSERT(reporter, texProxy->uniqueID().asUInt() != tex->uniqueID().asUInt());
    }

    REPORTER_ASSERT(reporter, tex->origin() == texProxy->origin());
    if (SkBackingFit::kExact == fit) {
        REPORTER_ASSERT(reporter, tex->width() == texProxy->width());
        REPORTER_ASSERT(reporter, tex->height() == texProxy->height());
    } else {
        REPORTER_ASSERT(reporter, tex->width() >= texProxy->width());
        REPORTER_ASSERT(reporter, tex->height() >= texProxy->height());
    }
    REPORTER_ASSERT(reporter, tex->config() == texProxy->config());
}

DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DeferredProxyTest, reporter, ctxInfo) {
    GrTextureProvider* provider = ctxInfo.grContext()->textureProvider();
    const GrCaps& caps = *ctxInfo.grContext()->caps();

    const GrGpuResource::UniqueID kInvalidResourceID = GrGpuResource::UniqueID::InvalidID();

    for (auto origin : { kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin }) {
        for (auto widthHeight : { 100, 128, 1048576 }) {
            for (auto config : { kAlpha_8_GrPixelConfig, kRGBA_8888_GrPixelConfig }) {
                for (auto fit : { SkBackingFit::kExact, SkBackingFit::kApprox }) {
                    for (auto budgeted : { SkBudgeted::kYes, SkBudgeted::kNo }) {
                        for (auto numSamples : { 0, 4}) {
                            bool renderable = caps.isConfigRenderable(config, numSamples > 0) &&
                                 numSamples <= caps.maxColorSampleCount();
                            bool allocable = widthHeight <= caps.maxTextureSize();

                            GrSurfaceDesc desc;
                            desc.fFlags = kRenderTarget_GrSurfaceFlag;
                            desc.fOrigin = origin;
                            desc.fWidth = widthHeight;
                            desc.fHeight = widthHeight;
                            desc.fConfig = config;
                            desc.fSampleCnt = numSamples;

                            if (renderable) {
                                sk_sp<GrSurfaceProxy> sProxy(GrSurfaceProxy::MakeDeferred(
                                                                                caps, desc,
                                                                                fit, budgeted));
                                REPORTER_ASSERT(reporter, allocable == SkToBool(sProxy));
                                if (sProxy) {
                                    REPORTER_ASSERT(reporter, sProxy->asRenderTargetProxy());
                                    // This forces the proxy to compute and cache its
                                    // pre-instantiation size guess. Later, when it is actually
                                    // instantiated, it checks that the instantiated size is <= to
                                    // the pre-computation. If the proxy never computed its
                                    // pre-instantiation size then the check is skipped.
                                    sProxy->gpuMemorySize();

                                    check_surface(reporter, sProxy.get(), origin,
                                                  widthHeight, widthHeight, config,
                                                  kInvalidResourceID, budgeted);
                                    check_rendertarget(reporter, caps, provider,
                                                       sProxy->asRenderTargetProxy(), numSamples,
                                                       fit, caps.maxWindowRectangles(), false);
                                }
                            }

                            desc.fFlags = kNone_GrSurfaceFlags;
                            desc.fSampleCnt = 0;

                            sk_sp<GrSurfaceProxy> sProxy(GrSurfaceProxy::MakeDeferred(caps,
                                                                                desc,
                                                                                fit,
                                                                                budgeted));
                            REPORTER_ASSERT(reporter, allocable == SkToBool(sProxy));
                            if (sProxy) {
                                // This forces the proxy to compute and cache its pre-instantiation
                                // size guess. Later, when it is actually instantiated, it checks
                                // that the instantiated size is <= to the pre-computation.
                                // If the proxy never computed its pre-instantiation size then the
                                // check is skipped.
                                sProxy->gpuMemorySize();

                                check_surface(reporter, sProxy.get(), origin,
                                              widthHeight, widthHeight, config,
                                              kInvalidResourceID, budgeted);
                                check_texture(reporter, provider, sProxy->asTextureProxy(),
                                              fit, false);
                            }
                        }
                    }
                }
            }
        }
    }
}

DEF_GPUTEST_FOR_RENDERING_CONTEXTS(WrappedProxyTest, reporter, ctxInfo) {
    GrTextureProvider* provider = ctxInfo.grContext()->textureProvider();
    const GrCaps& caps = *ctxInfo.grContext()->caps();

    static const int kWidthHeight = 100;

    for (auto origin : { kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin }) {
        for (auto config : { kAlpha_8_GrPixelConfig, kRGBA_8888_GrPixelConfig }) {
            for (auto budgeted : { SkBudgeted::kYes, SkBudgeted::kNo }) {
                for (auto numSamples: { 0, 4}) {
                    bool renderable = caps.isConfigRenderable(config, numSamples > 0);

                    GrSurfaceDesc desc;
                    desc.fOrigin = origin;
                    desc.fWidth = kWidthHeight;
                    desc.fHeight = kWidthHeight;
                    desc.fConfig = config;
                    desc.fSampleCnt = numSamples;

                    // External on-screen render target.
                    if (renderable && kOpenGL_GrBackend == ctxInfo.backend()) {
                        GrBackendRenderTargetDesc backendDesc;
                        backendDesc.fWidth = kWidthHeight;
                        backendDesc.fHeight = kWidthHeight;
                        backendDesc.fConfig = config;
                        backendDesc.fOrigin = origin;
                        backendDesc.fSampleCnt = numSamples;
                        backendDesc.fStencilBits = 8;
                        backendDesc.fRenderTargetHandle = 0;

                        sk_sp<GrRenderTarget> defaultFBO(
                            provider->wrapBackendRenderTarget(backendDesc));

                        sk_sp<GrSurfaceProxy> sProxy(GrSurfaceProxy::MakeWrapped(defaultFBO));
                        check_surface(reporter, sProxy.get(), origin,
                                      kWidthHeight, kWidthHeight, config,
                                      defaultFBO->uniqueID(), SkBudgeted::kNo);
                        check_rendertarget(reporter, caps, provider, sProxy->asRenderTargetProxy(),
                                           numSamples, SkBackingFit::kExact, 0, true);
                    }

                    sk_sp<GrTexture> tex;

                    // Internal offscreen render target.
                    if (renderable) {
                        desc.fFlags = kRenderTarget_GrSurfaceFlag;
                        tex.reset(provider->createTexture(desc, budgeted));
                        sk_sp<GrRenderTarget> rt(sk_ref_sp(tex->asRenderTarget()));

                        sk_sp<GrSurfaceProxy> sProxy(GrSurfaceProxy::MakeWrapped(rt));
                        check_surface(reporter, sProxy.get(), origin,
                                      kWidthHeight, kWidthHeight, config,
                                      rt->uniqueID(), budgeted);
                        check_rendertarget(reporter, caps, provider, sProxy->asRenderTargetProxy(),
                                           numSamples, SkBackingFit::kExact,
                                           caps.maxWindowRectangles(), true);
                    }

                    if (!tex) {
                        SkASSERT(kNone_GrSurfaceFlags == desc.fFlags );
                        desc.fSampleCnt = 0;
                        tex.reset(provider->createTexture(desc, budgeted));
                    }

                    sk_sp<GrSurfaceProxy> sProxy(GrSurfaceProxy::MakeWrapped(tex));
                    check_surface(reporter, sProxy.get(), origin,
                                  kWidthHeight, kWidthHeight, config, tex->uniqueID(), budgeted);
                    check_texture(reporter, provider, sProxy->asTextureProxy(),
                                  SkBackingFit::kExact, true);
                }
            }
        }
    }
}

#endif
<|endoftext|>