From f21ffedaba671b481fc7c0d05c96ec8e259b7b0e Mon Sep 17 00:00:00 2001 From: Martin Sulzmann Date: Tue, 10 Dec 2024 13:41:54 +0100 Subject: [PATCH] Delete lec-cpp-advanced-quick-check.html --- lec-cpp-advanced-quick-check.html | 796 ------------------------------ 1 file changed, 796 deletions(-) delete mode 100644 lec-cpp-advanced-quick-check.html diff --git a/lec-cpp-advanced-quick-check.html b/lec-cpp-advanced-quick-check.html deleted file mode 100644 index 28783b9..0000000 --- a/lec-cpp-advanced-quick-check.html +++ /dev/null @@ -1,796 +0,0 @@ - - - - - - - - - QuickCheck in C++ - - - - - -
-

QuickCheck in C++

-

-Martin Sulzmann -

-
-
-

Testen = Dynamische Programm Analyse

-

Niemand schreibt gerne Tests! Tests sind aber notwendig. Ohne Tests -bleiben bugs oft unentdeckt und sind schlecht reproduzierbar.

-

Klassischer Testansatz: Unit -Tests

- -

Siehe Beispiel “count”.

-

In der Regel viel händische Arbeit notwendig.

-

Testen im Stile von -QuickCheck

-
    -
  1. Automatische Generierung von Testdaten (“type-specific test -(input) data generation”)

  2. -
  3. Eigenschafts-basiertes Testen (“property-based testing”)

  4. -
-

Punkt 1: Automatische Ausführung der “unit”.

-

Punkt 2: Automatische Überprüfung auf erwartetes -Programmverhalten.

-

Beide Punkte wurden populär gemacht durch die Haskell Bibliothek QuickCheck.

-

Wir zeigen hier die Ideen hinter QuickCheck im Kontext von C++. Die -QuickCheck Idee wurde auf viele Programmiersprachen portiert. Siehe QuickCheck for other -languages

-

Wir betrachten zwei Umsetzungen der QuickCheck Idee in C++:

-
    -
  1. Eine Umsetzung welche OO (Vererbung) verwendet

  2. -
  3. Eine weitere Umsetzung welche funktionale Abstraktionen verwendet -und sehr nahe an der originalen Haskell Version ist.

  4. -
-
-
-

Motivierendes Beispiel “count”

- -

-#include <cstdlib>
-#include <iostream>
-#include <string>
-#include <functional>
-
-using namespace std;
-
-
-// Recall
-
-char* skipB(char* input) {
-  while(*input == ' ')
-    input++;
-  return input;
-}
-
-char* scanW(char* input) {
-  while(*input != '\0' && *input != ' ')
-    input++;
-  return input;
-}
-
-
-int count(char* input) {
-  int cnt = 0;
-
-  while(*input != '\0') {
-    input = skipB(input);
-    if(*input != '\0') {
-      cnt++;
-      input = scanW(input);
-    }
-  }
-  return cnt;
-}
-
-int countString(string s) {
- char cString[s.size() + 1];
-
- s.copy(cString, s.size() + 1);
- cString[s.size()] = '\0';
-
- return count(cString);
-}
-
-
-// User style testing
-
-void testCount(int count(string)) {
-
-  auto test = [count](string s) {
-    cout << "\n" << s << " => " << count(s);
-  };
-
-  test("");
-
-  test("hallo");
-
-  test(" hallo");
-
-  test(" hal lo");
-
-
-}
-
-// Unit testing
-
-void unitTestCount(int count(string)) {
-
-  auto test = [count](string s, int expected) {
-    auto res = count(s) == expected ? "okay" : "fail";
-    cout << "\n" << s << " => " << res;
-  };
-
-  test("",0);
-
-  test("hallo",1);
-
-  test(" hallo", 1);
-
-  test(" hal lo", 2);
-
-
-}
-
-int main() {
-
-  cout << "\ncountString:";
-  testCount(countString);
-
-  unitTestCount(countString);
-}
-
-
-

QuickCheck Idee

- -

Generiere

-

Wie?

- -

Nein!

- -

Prüfe Eigenschaften

-

Wie?

- -

Nein!

- -

Wie sehen diese Eigenschaften aus?

- -
-
-

Umsetzung von QuickCheck mit Hilfe von OO

-
template<typename T>
-class GenOO {
-public:
-  virtual T generate() = 0;
-};
-
-
-template<typename T>
-void quickCheckOO(GenOO<T>* g, bool p(T)) {
-
-for(int i =0; i < 20; i++) {
-    auto b = p(g->generate());
-    if (b) {
-      cout << "\n +++ OK";
-    } else {
-      cout << "\n *** Failed";
-    }
-  }
-
-}
- -

Generatoren Instanzen

-
class Bool : public GenOO<bool> {
-public:
-  bool generate() {
-    return (rand() % 2 == 2) ? true : false;
-  }
-};
-
-
-class Char : public GenOO<char> {
-public:
-  char generate() {
-    return (char)(rand() % 255);
-  }
-};
-
-class String : public GenOO<string> {
-public:
-  string generate() {
-    int n = rand() % 15;
-    string s;
-    Char g;
-    for(int i=0; i < n; i++) {
-      s = s + g.generate();
-    }
-    return s;
-  }
-};
-

Eigenschaften

-
bool prop1(string s) {
-  return countString(s) >= 0;
-}
-
-bool prop2(string s) {
-  string sRev;
-
-  sRev = s;
-  reverse(sRev.begin(), sRev.end());
-
-  return (countString(s) == countString(sRev));
-}
-
-bool prop3(string s) {
-  string s2;
-
-  s2 = s + s;
-
-  return (2 * countString(s) == countString(s2));
-}
-

Gelten alle Eigenschaften immer?

-

“QuickCheck count”

-
void quickCheckOOCount() {
-
-
-  cout << "\n**** prop1 \n";
-  String* g = new String();
-
-  quickCheckOO<string>(g, prop1);
-
-  cout << "\n**** prop2 \n";
-  quickCheckOO<string>(g, prop2);
-
-
-  cout << "\n**** prop3 \n";
-  quickCheckOO<string>(g, prop3);
-
-  delete g;
-
-}
-

prop3 fails

-

Warum? Betrachte z.B. "Hallo".

-

Betrachte folgende Korrekturen.

-
bool prop3a(string s) {
-  string s2;
-
-  s2 = s + s;
-
-  return (2 * countString(s) >= countString(s2));
-}
-
-
-bool prop3b(string s) {
-  string s2;
-
-  s2 = s + " " + s;
-
-  return (2 * countString(s) == countString(s2));
-}
-

prop3a gilt immer.

-

Wieso gilt prop3b nicht immer?

- -
-
-

Umsetzung von QuickCheck mit Hilfe funktionaler Abstraktion

-

Eine Generatorenobjekt ist jetzt nichts anderes wie eine Funktion -welche eine zufälligen Wert vom Typ T liefert.

-

Wie generieren wir Generatorenobjekt für verschiedene Instanzen von -T, z.b. char, string, …?

-

Wir verwenden dazu arbitrary Funktionen. Diese -Funktionen unterscheiden sich nur im Rückgabetype. Solch eine Form der -Überladung ist eigentlich in C++ nicht möglich. Wir können solch eine -Überladungsform aber mit Hilfe von Template Instanzierung emulieren.

-
template<typename T>
-class Gen {
- public:
-  Gen() {};
-  Gen(std::function<T()> gen_) { gen = std::function<T()>(gen_); }
-  std::function<T()> gen;
-  T generate() { return gen(); };
-};
-
-
-template<typename T>
-Gen<T> arbitrary();
-
-template<typename T>
-void quickCheck(bool p(T)) {
-  Gen<T> g = arbitrary<T>();
-
-for(int i =0; i < 20; i++) {
-    auto b = p(g.generate());
-    if (b) {
-      cout << "\n +++ OK";
-    } else {
-      cout << "\n *** Failed";
-    }
-  }
-
-}
-

Beachte:

- -

Generatoren Instanzen

-
template<typename T>
-Gen<T> arbitrary();
-
-template<>
-Gen<bool> arbitrary() { return Gen<bool>([] () -> bool { return (rand() % 2 == 0) ? true : false; }); }
-
-template<>
-Gen<char> arbitrary() { return Gen<char>([] () -> char { return (char)(rand() % 255); }); }
-
-template<>
-Gen<string> arbitrary() {
-
-  return Gen<string>(
-             [] () -> string {
-               int n = rand() % 15;
-               string s;
-               Gen<char> g = arbitrary<char>();
-               for(int i=0; i<n; i++) {
-             s = s + g.generate();
-               }
-               return s;
-             }
-             );
-}
-

“QuickCheck count”

-
void quickCheckCount() {
-
-  cout << "\n**** prop1 \n";
-  quickCheck<string>(prop1);
-
-  cout << "\n**** prop2 \n";
-  quickCheck<string>(prop2);
-
-
-  cout << "\n**** prop3 \n";
-  quickCheck<string>(prop3);
-
-}
-

Im Vergleich zur “OO” Version sind weniger Parameter notwendig.

-
-
-

Kompletter Programmcode

-

-#include <cstdlib>
-#include <iostream>
-#include <string>
-#include <functional>
-#include <algorithm>
-
-using namespace std;
-
-/////////////////////////////////////////////
-// Zaehlen von Woertern
-
-char* skipB(char* input) {
-  while(*input == ' ')
-    input++;
-  return input;
-}
-
-char* scanW(char* input) {
-  while(*input != '\0' && *input != ' ')
-    input++;
-  return input;
-}
-
-
-int count(char* input) {
-  int cnt = 0;
-
-  while(*input != '\0') {
-    input = skipB(input);
-    if(*input != '\0') {
-      cnt++;
-      input = scanW(input);
-    }
-  }
-  return cnt;
-}
-
-int countString(string s) {
- char cString[s.size() + 1];
-
- s.copy(cString, s.size() + 1);
- cString[s.size()] = '\0';
-
- return count(cString);
-}
-
-
-/////////////////////////////////////////////
-// User Tests
-
-void testCount(int count(string)) {
-
-  auto test = [count](string s) {
-    cout << "\n" << s << " => " << count(s);
-  };
-
-  test("");
-
-  test("hallo");
-
-  test(" hallo");
-
-  test(" hal lo");
-
-
-}
-
-/////////////////////////////////////////////
-// Unit Tests
-
-void unitTestCount(int count(string)) {
-
-  auto test = [count](string s, int expected) {
-    auto res = count(s) == expected ? "okay" : "fail";
-    cout << "\n" << s << " => " << res;
-  };
-
-  test("",0);
-
-  test("hallo",1);
-
-  test(" hallo", 1);
-
-  test(" hal lo", 2);
-
-  test(" hal looo", 3);
-  // Tests koennen auch Fehlerhaft sein!
-
-
-}
-
-
-void runTests() {
-
-  testCount(countString);
-
-  unitTestCount(countString);
-
-}
-
-
-/////////////////////////////////////////////
-// QuickCheck unter Verwendung von OO
-
-
-// Eigenschaften
-
-bool prop1(string s) {
-  return countString(s) >= 0;
-}
-
-bool prop2(string s) {
-  string sRev;
-
-  sRev = s;
-  reverse(sRev.begin(), sRev.end());
-
-  return (countString(s) == countString(sRev));
-}
-
-bool prop3(string s) {
-  string s2;
-
-  s2 = s + s;
-
-  return (2 * countString(s) == countString(s2));
-}
-
-
-
-// Testdaten Generatoren
-
-template<typename T>
-class GenOO {
-public:
-  virtual T generate() = 0;
-};
-
-
-// Instanzen
-
-class Bool : public GenOO<bool> {
-public:
-  bool generate() {
-    return (rand() % 2 == 2) ? true : false;
-  }
-};
-
-
-class Char : public GenOO<char> {
-public:
-  char generate() {
-    return (char)(rand() % 255);
-  }
-};
-
-class String : public GenOO<string> {
-public:
-  string generate() {
-    int n = rand() % 15;
-    string s;
-    Char g;
-    for(int i=0; i < n; i++) {
-      s = s + g.generate();
-    }
-    return s;
-  }
-};
-
-
-// Generierung von strings
-
-void genStringOO() {
-  String x;
-
-  for (int i = 1; i < 10; i ++) {
-    cout << "\n" + x.generate();
-  }
-
-}
-
-
-template<typename T>
-void quickCheckOO(GenOO<T>* g, bool p(T)) {
-
-for(int i =0; i < 20; i++) {
-    auto b = p(g->generate());
-    if (b) {
-      cout << "\n +++ OK";
-    } else {
-      cout << "\n *** Failed";
-    }
-  }
-
-}
-
-
-// QuickCheck count
-
-void quickCheckOOCount() {
-
-
-  cout << "\n**** prop1 \n";
-  String* g = new String();
-
-  quickCheckOO<string>(g, prop1);
-
-  cout << "\n**** prop2 \n";
-  quickCheckOO<string>(g, prop2);
-
-
-  cout << "\n**** prop3 \n";
-  quickCheckOO<string>(g, prop3);
-
-  delete g;
-
-}
-
-/////////////////////////////////////////////
-// QuickCheck mit Hilfe funktionaler Abstraktion
-
-template<typename T>
-class Gen {
- public:
-  Gen() {};
-  Gen(std::function<T()> gen_) { gen = std::function<T()>(gen_); }
-  std::function<T()> gen;
-  T generate() { return gen(); };
-};
-
-
-template<typename T>
-Gen<T> arbitrary();
-
-template<>
-Gen<bool> arbitrary() { return Gen<bool>([] () -> bool { return (rand() % 2 == 0) ? true : false; }); }
-
-template<>
-Gen<char> arbitrary() { return Gen<char>([] () -> char { return (char)(rand() % 255); }); }
-
-template<>
-Gen<string> arbitrary() {
-
-  return Gen<string>(
-             [] () -> string {
-               int n = rand() % 15;
-               string s;
-               Gen<char> g = arbitrary<char>();
-               for(int i=0; i<n; i++) {
-             s = s + g.generate();
-               }
-               return s;
-             }
-             );
-}
-
-
-void genString() {
-  Gen<string> x = arbitrary<string>();
-
-  for (int i = 1; i < 10; i ++) {
-    cout << "\n" + x.generate();
-  }
-
-}
-
-template<typename T>
-void quickCheck(bool p(T)) {
-  Gen<T> g = arbitrary<T>();
-
-for(int i =0; i < 20; i++) {
-    auto b = p(g.generate());
-    if (b) {
-      cout << "\n +++ OK";
-    } else {
-      cout << "\n *** Failed";
-    }
-  }
-
-}
-
-
-void quickCheckCount() {
-
-  cout << "\n**** prop1 \n";
-  quickCheck<string>(prop1);
-
-  cout << "\n**** prop2 \n";
-  quickCheck<string>(prop2);
-
-
-  cout << "\n**** prop3 \n";
-  quickCheck<string>(prop3);
-
-}
-
-
-int main() {
-
-  // runTests();
-
-  // genStringOO();
-
-  // quickCheckOOCount();
-
-  // genString();
-
-  // quickCheckCount();
-}
-
- -