risorse | good unit tests

Good Unit Tests /15

Questa parte (la quindicesima; qui la prima, qui la seconda, qui la terza, qui la quarta, qui la quinta, qui la sesta, qui la settima, qui l'ottava, qui la nona, qui la decima, qui l'undicesima, qui la dodicesima, qui la tredicesima e qui la quattordicesima) risolve un problema relativo alla modalità GUT_ENABLE_FAILFAST e introduce il supporto dei colori nella finestra del terminale di Linux – Ubuntu 14.04 Desktop.

Fix di GUT_ENABLE_FAILFAST

Durante la rifattorizzazione che ha originato le classi Listener/Report ha introdotto un problema piuttosto grave: abilitando l'opzione fail-fast, la test suite termina sì al primo errore, ma con successo. Il codice va così modificato:

class Listener {
  // ...
  void quit(const std::string& reason) { report_->quit(reason); }
    ++failedTestCount_;
    report_->quit(reason);
  }
  // ...
};

Colori nella console di Linux

La finestra del terminale di Linux supporta le sequenze ANSI[2], ed è perciò sufficiente emettere i codici escape opportuni per cambiare il colore del testo. Trattandosi di fatto di caratteri extra che potrebbero rendere più difficoltoso un eventuale parsing del prospetto prodotto dall'oggetto Report, conviene rendere opzionale la loro presenza. L'implementazione del controllo può avvenire con la stessa modalità con cui è stato realizzato il flag GUT_ENABLE_FAILFAST:

namespace gut {

// static flag, initially reset; declare an instance to set it
template<class T>
class StaticFlag {
  static bool flag_;
public:
  StaticFlag() {
    flag_ = true;
  }
  static bool enabled() {
    return flag_;
  }
};

// enable/disable colors in console
template<class T>
bool StaticFlag<T>::flag_ = false;

namespace color {

struct ColorInConsole_ { };
typedef StaticFlag<ColorInConsole_> ColorInConsole;

#define GUT_ENABLE_COLORINCONSOLE gut::color::ColorInConsole colorInConsole_;

} // namespace color

} // namespace gut

#include "colors.h"

#ifdef _WIN32
#include "windows/colors.h"
#else
#include "linux/colors.h"
#endif

// ...

class FailFast {
  static bool enabled_;
public:
  FailFast() {
    enabled_ = true;
  }
  static bool enabled() {
    return enabled_;
  }
};

bool FailFast::enabled_ = false;

struct FailFast_ { };
typedef StaticFlag<FailFast_> FailFast;

L'implementazione dei due flag ColorInConsole e FailFast per mezzo di una classe generica comune e un tipo ausiliario vuoto che consente l'istanziazione differenziata del membro statico è nota come tag dispatching[1][3].

Il supporto del testo colorato in Linux è banale:

// file linux/colors.h
#ifndef COLORS_H
#define COLORS_H

namespace gut {

namespace color {

std::ostream& setForegroundColor(std::ostream& os, const char* code) {
  if (ColorInConsole::enabled())
    os << "\x1b[" << code << "m";
  return os;
}

std::ostream& black(std::ostream& os) {
  return setForegroundColor(os, "30");
}

std::ostream& navy(std::ostream& os) {
  return setForegroundColor(os, "34");
}

std::ostream& green(std::ostream& os) {
  return setForegroundColor(os, "32");
}

std::ostream& teal(std::ostream& os) {
  return setForegroundColor(os, "36");
}

std::ostream& maroon(std::ostream& os) {
  return setForegroundColor(os, "31");
}

std::ostream& purple(std::ostream& os) {
  return setForegroundColor(os, "35");
}

std::ostream& olive(std::ostream& os) {
  return setForegroundColor(os, "33");
}

std::ostream& silver(std::ostream& os) {
  return setForegroundColor(os, "37");
}

std::ostream& gray(std::ostream& os) {
  return setForegroundColor(os, "90");
}

std::ostream& blue(std::ostream& os) {
  return setForegroundColor(os, "94");
}

std::ostream& lime(std::ostream& os) {
  return setForegroundColor(os, "92");
}

std::ostream& aqua(std::ostream& os) {
  return setForegroundColor(os, "96");
}

std::ostream& red(std::ostream& os) {
  return setForegroundColor(os, "91");
}

std::ostream& fuchsia(std::ostream& os) {
  return setForegroundColor(os, "95");
}

std::ostream& yellow(std::ostream& os) {
  return setForegroundColor(os, "93");
}

std::ostream& white(std::ostream& os) {
  return setForegroundColor(os, "97");
}

std::ostream& reset(std::ostream& os) {
  return setForegroundColor(os, "0");
}

} // namespace color

} // namespace gut

#endif // COLORS_H

Anche l'implementazione dell'oggetto Console per Windows va modificata in modo da colorare il testo solo quando richiesto:

// file windows/colors.h (was: colors.h)

// ...

namespace gut {

namespace color {

class WindowsConsole_ {
  HANDLE handle_;
  WORD defaultAttrs_;
  static const WORD mask_ =
    FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY;
public:
  WindowsConsole_()
   : handle_(GetStdHandle(STD_OUTPUT_HANDLE))
   , defaultAttrs_(getAttrs()) {
  }
  void setColor(WORD color) {
    if (ColorInConsole::enabled())
      SetConsoleTextAttribute(handle_, (getAttrs() & ~mask_) | color);
  }
  void resetColors() {
    if (ColorInConsole::enabled())
      SetConsoleTextAttribute(handle_, defaultAttrs_);
  }
protected:
  WORD getAttrs() {
    CONSOLE_SCREEN_BUFFER_INFO info;
    GetConsoleScreenBufferInfo(handle_, &info);
    return info.wAttributes;
  }
};

WindowsConsole_& theConsole() {
  static WindowsConsole_ console;
  return console;
}

// ...

} // namespace color

} // namespace gut

#endif // COLORS_H

L'uso dei colori nella console è quindi normalmente disattivato; per attivarlo è necessario inserire la direttiva GUT_ENABLE_COLORINCONSOLE all'inizio della test suite:

// file example.cpp
// ...

GUT_ENABLE_COLORINCONSOLE

TEST("Initial list is empty") {
    RecentlyUsedList anEmptyList;

    CHECK(anEmptyList.empty());
    CHECK(anEmptyList.size() == 0);
}

// ...

Codice sorgente

Riferimenti

  1. Schmidt, B. “Alternatives to Singletons and Global Variables”, Overload Journal #126 - April 2015 — <http://accu.org/index.php/journals/2085>, visitato il 29/04/2015.
  2. “ANSI escape code”, wikipedia.org — <http://en.wikipedia.org/wiki/ANSI_escape_code>, visitato il 29/04/2015.
  3. “Generic Programming Techniques – Tag Dispatching”, boost.org — <http://www.boost.org/community/generic_programming.html#tag_dispatching>, visitato il 29/04/2015.

Pagina modificata il 29/04/2015