Donnerstag, 3. November 2011

1000 Testrechnungen in 80 Millisekunden

Die Bresche ist nun geschlagen:

  • Ein Programm testgen – das mit einer Referenzversion der swedll32.dll aus dem Release 1.77 arbeitet – berechnet zu zehn verschiedenen astrologischen Planeten und je 100 verschiedenen Daten die geozentrischen ekliptikalen Positionen und speichert die Ergebnisrecords binär (mittels fwrite) in einer Datei testdata.bin ab.
  • Das Programm tests enthält eine Testklasse TestGeneratedData, die diese Daten einliest, die Berechnungen mit der aktuell in Bearbeitung befindlichen DLL ausführt und die Ergebnisse mit den Erwartungen vergleicht. Abweichungen werden in detaillierter, nachvollziehbarer Form in eine Logdatei testFailures.txt geschrieben. Für 1000 Rechnungen benötigt das Testprogramm 80 Millisekunden. Eine erträgliche Zeit, um sie bei Builds mit auszuführen.

In der Datei folgen ein allgemeiner testHeader, ein Header swissCalcHeader für die Tests, die speziell der Funktion swe_calc gewidmet sind, sowie eine Anzahl von Records swissCalcData - für jede auszuführende Rechnung einer.
// A test
typedef struct {
// Specifies the particular kind of the test (e.g. a test of swe_calc):
int type;
char description[100];
// The number of bytes belonging to this test (for skips):
int length;
} testHeader;

// A set of swe_calc computations
typedef struct {
// flags for swe_calc
int flags;
// precision exponent 10**p as factor to DEFAULT_PRECISION :
int precision[6];
int numberOfRecords;
} swissCalcHeader;

// A single swe_calc computation
typedef struct {
double jd;
int planet;
double result[6];
} swissCalcData;

Hier der Code der Testklasse für die Funktion swe_calc:
class TestGeneratedData : public Test {
public :
virtual void run() {
testHeader header;
swissCalcHeader scHeader;
FILE *testdata = fopen( "testdata.bin","rb" );
if (!testdata) {
fail( "Can't open testdata.bin" );
}
fread( &header, sizeof(header), 1, testdata );
fread( &scHeader, sizeof(scHeader), 1, testdata );

swissCalcData scData[scHeader.numberOfRecords];
fread( &scData,
sizeof(swissCalcData),
scHeader.numberOfRecords,
testdata );
SwissCalc sc;
bool ok = true;
for (int i=0;i<scHeader.numberOfRecords;i++) {
if (!sc.calcsAsExpected(
scData[i].jd, scData[i].planet, scHeader.flags, scData[i].result
)) {
fail( "Differences", sc.diffs );
}
}
fclose( testdata );
}
};

Der File I/O muss noch in andere Funktionen ausgelagert werden (anderer Concern) und gegen Fehler abgesichert werden (Rückgabewerte von fread auswerten). Die eigentliche Rechenarbeit steckt in der Funktion SwissCalc::calcsAsExpected, die ich ja früher schon diskutiert hatte.
Noch offen:

  • Das Programm bricht momentan bei der ersten Rechnung ab, die Differenzen ergab. Der Programmfluss soll so geändert werden, dass trotz Differenzen weitergerechnet wird: Es soll alles protokolliert werden, und am Schluss bekommt dieser Test in der Standardausgabe den Status not ok.
  • Damit grössere Datenmengen eingelesen werden, soll eine Paketbildung gemacht werden, wonach die Daten z.B. in 1000er Päckchen eingelesen und ausgeführt werden.
  • Der Name der Testdatendatei soll im Programm als Parameter angegeben werden können. Er wird dann vom Testrunner an die Testklasse durchgereicht (oder die Testklasse kann diesen Dateinamen beim Testrunner erfragen).



Weiteres Vorgehen: Nun beginnt die eher systematische Arbeit, viele Testfälle zu erfassen. Die Tests sollten eine Zeitreise durch die Jahrhunderte machen und auch weniger verwendete (Klein-)Planeten umfassen. Die verschiedenen Koordinatensysteme, die man anfordern darf, sollten ausgetestet werden - ebenso andere Optionen wie das Speed-Flag.

Solange ein solcher Rundumschlag durch die möglichen Aufrufe noch fehlt, sind Änderungen am Quellcode tabu (sosehr es mich danach gelüstet, schon jetzt Hand anzulegen...).

Keine Kommentare:

Kommentar veröffentlichen