// ConsoleTestRun03.cpp : 頑健なテスト駆動ルーチン
//
// / GS / GL / analyze - / W3 / Gy / Zc:wchar_t / Zi / Gm - / O2 / Fd"Release\vc140.pdb" / Zc : inline / fp : precise / D "WIN32" / D "NDEBUG" / D "_CONSOLE" / D "_UNICODE" / D "UNICODE" / fp : except / errorReport : prompt / WX - / Zc : forScope / Gd / Oy - / Oi / MD / Fa"Release\" /EHsc /nologo /Fo"Release\" /FAcs /Fp"Release
//
#ifdef _MSC_VER
#include "stdafx.h"
#include <float.h>
#include <excpt.h>
#else
#include <stdexcept>
#include <execinfo.h>
#include <unistd.h>
#endif
#include <string>
#include <errno.h>
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <functional>
#include <chrono> // std::chrono::seconds
#include <ratio>
#include <thread>
#include <future>
#include <exception>
#include <cstdlib>
#include <cstring>
#include <memory>
#include <csignal>
//#include <new>
//#include <cmath>
//#include <cfloat>
#include <vector>
#include <random>
//環境依存関数のラッピング
#define TestStatusStoreValue(x,y) #x #y
#ifdef _MSC_VER
#define myStrCopy(dest,src) strcpy_s(dest,src)
#define myStrCat(dest,src) strcat_s(dest,src)
#else
#define myStrCopy(dest,src) strcpy(dest,src)
#define myStrCat(dest,src) strcat(dest,src)
#endif
//テスト検証 補助用関数
void memDump(void * a, int offset, int len) {
void * curSP;
#ifdef _MSC_VER
__asm {
mov curSP, esp
}
#else
__asm__("mov %%esp,%0" : "=A" (curSP));
#endif
unsigned int *x = ((unsigned int *)(((int)a + offset) & 0xfffffff0)); // 4バイト
unsigned int *e = ((unsigned int *)((char *)a + offset + len));
if ((void *)x > (void *)((char*)curSP - 16)) x = (unsigned int *)(((int)curSP - 16) & 0xfffffff0);
if ((void *)x > (void *)((char*)&a - 16)) x = (unsigned int *)(((int)&a - 16) & 0xfffffff0);
if ((void *)e < (void *)&x) e = (unsigned int *)&x;
std::cout << std::hex << std::endl;
std::cout.width(16); std::cout.fill('0');
std::cout << curSP << std::endl;
std::cout << a << std::endl;
std::cout << &a << std::endl;
do {
std::cout << std::setw(12) << std::setfill('0') << x << ": ";
std::cout << std::setw(8) << std::setfill('0') << x[0]
<< ' ' << std::setw(8) << std::setfill('0') << x[1]
<< ' ' << std::setw(8) << std::setfill('0') << x[2]
<< ' ' << std::setw(8) << std::setfill('0') << x[3] << std::endl;
x += 4;
} while (x <= e);
}
//要素数9個固定ソートasm版
#ifdef _MSC_VER
#define CONDSWAPA(p,q) __asm{ \
__asm mov eax,[ebp+p*4] \
__asm mov ebx,[ebp+q*4] \
__asm cmp eax,ebx \
__asm cmovl ebx,eax \
__asm cmovl eax,[ebp+q*4] \
__asm mov [ebp+p*4],eax \
__asm mov [ebp+q*4],ebx \
}
#define CONDSWAPC(p,q) __asm{ \
__asm mov ecx,[ebp+p*4] \
__asm mov edx,[ebp+q*4] \
__asm cmp ecx,edx \
__asm cmovl edx,ecx \
__asm cmovl ecx,[ebp+q*4] \
__asm mov [ebp+p*4],ecx \
__asm mov [ebp+q*4],edx \
}
#else
// for linux gcc/x86
#define CONDSWAPA(p,q) __asm__ ( \
"mov %0, %%eax;" \
"mov %1, %%ebx;" \
"cmp %%ebx, %%eax;" \
"cmovl %%ebx,%0 ;" \
"cmovl %%eax,%1 ;" \
:"+r"(d[p]),"+r"(d[q]): \
: "%eax", "%ebx", "memory" );
#define CONDSWAPC(p,q) __asm__ ( \
"mov %0, %%ecx;" \
"mov %1, %%edx;" \
"cmp %%edx, %%ecx;" \
"cmovl %%edx,%0 ;" \
"cmovl %%ecx,%1 ;" \
:"+r"(d[p]),"+r"(d[q]): \
: "%ecx", "%edx", "memory" );
#endif
//#undef CONDSWAPA
//#undef CONDSWAPC
//#define CONDSWAPA(p,q) {register unsigned int w;if(d[p]>d[q]){w=d[p];d[p]=d[q];d[q]=w;}}
//#define CONDSWAPC(p,q) {register unsigned int w;if(d[p]>d[q]){w=d[p];d[p]=d[q];d[q]=w;}}
#define ASMSORT4(a,b,c,d) \
CONDSWAPA(a,b); \
CONDSWAPC(c,d); \
CONDSWAPC(b,c); \
CONDSWAPA(a,b); \
CONDSWAPC(c,d);
int sort9l(void *v) {
int *d = (int *)v;
#ifdef _MSC_VER
#pragma warning(disable : 4731)
__asm {
push ebp
mov ebp, d
}
#endif
ASMSORT4(0, 1, 2, 3);
ASMSORT4(4, 5, 6, 7);
CONDSWAPA(0, 4);
CONDSWAPC(1, 5);
CONDSWAPA(2, 6);
CONDSWAPC(3, 7);
CONDSWAPA(7, 8);
CONDSWAPC(5, 7);
CONDSWAPA(4, 6);
CONDSWAPC(1, 3);
CONDSWAPA(0, 2);
CONDSWAPC(3, 5);
CONDSWAPA(2, 4);
ASMSORT4(0, 1, 2, 3);
ASMSORT4(4, 5, 6, 7);
CONDSWAPA(3, 4);
CONDSWAPA(2, 3);
CONDSWAPA(4, 5);
CONDSWAPA(3, 4);
#ifdef _MSC_VER
__asm {
pop ebp
}
#pragma warning(default : 4731)
#endif
return 0;
}
//順列生成しながら降順ソートをテストする
template<typename T, typename U,typename V> int testByGenPerm(size_t n, T testData, int(*testFunc)(V)) {
unsigned int i, k, c[9 + 1];
for (i = 0; i <= n; i++) c[i] = i;
int iter = 0;
k = 1;
while (k < n) {
if (k & 1) { i = c[k]; }
else { i = 0; }
{U w; w = testData[k]; testData[k] = testData[i]; testData[i] = w; } //1件のデータ生成のオーバヘッドを最小化
if ((*testFunc)(testData)) return 1;
iter++;
for (k = 1; k < n; k++) if (testData[k - 1] < testData[k]) return 1;//降順でなければFailed
for (k = 1; (c[k] == 0); k++) { c[k] = k; }
c[k]--;
}
std::cout << n << "個のデータを" << iter << "回 ソート実行完了." << std::endl;
return 0;
}
//ここまでテスト検証 補助用関数
/////ここから非テストコード毎の宣言
int testCase1(void);
int testCase2(void);
int testCase3(void);
int testCase4(void);
int testCase5(void);
int testCase6_Ok(std::vector<int> & toBeSortedData);
int testCase6_NG(std::vector<int> & toBeSortedData);
int testCase7(std::vector<float> & toBeSortedData);
/////ここまで非テストコード
/////ここから非テストコード毎の検証コード
int verifyCase1() { return testCase1(); }
int verifyCase2() { return testCase2(); }
int verifyCase3() { return testCase3(); }
int verifyCase4() { return testCase4(); }
int verifyCase5() { return testCase5(); }
template<typename T> int verifySort(int(*testFunc)(std::vector<T> &)) {
std::vector<T> toBeSrtedData{ 2,3,5,7,11,13,17,19,23 };
return testByGenPerm<std::vector<T>, T, std::vector<T> &>(9, toBeSrtedData, testFunc);
}
int verifyCase6_Ok() { //整数9個の9の階乗 全件テスト
return verifySort<int>(&testCase6_Ok);
}
int verifyCase6_NG() { //整数9個の9の階乗 全件テスト
return verifySort<int>(&testCase6_NG);
}
int verifyCase7() { //float 9個、1万種類 ランダムテスト
const int n = 9;
std::random_device rnd; // 非決定的な乱数生成器でシード生成機を生成
std::mt19937 mt(rnd()); // メルセンヌツイスターの32ビット版、引数は初期シード
std::uniform_real_distribution<double> rand100(0.0, 1024.0); // [0, 1024] 範囲の一様乱数
int iter;
for (iter = 0; iter < 2000; iter++) {
std::vector<float> toBeSrtedData;
for (int i = 0; i < n; ++i) {
toBeSrtedData.push_back((float)(rand100(mt)));
}
testCase7(toBeSrtedData); //降順テストの実装を呼び出す
if (toBeSrtedData[0] < toBeSrtedData[1]
|| toBeSrtedData[1] < toBeSrtedData[2]
|| toBeSrtedData[2] < toBeSrtedData[3]
|| toBeSrtedData[3] < toBeSrtedData[4]
|| toBeSrtedData[4] < toBeSrtedData[5]
|| toBeSrtedData[5] < toBeSrtedData[6]
|| toBeSrtedData[6] < toBeSrtedData[7]
|| toBeSrtedData[7] < toBeSrtedData[8]) {//降順ソートできていなければ、中断
//memDump(toBeSrtedData.data(), 0, 4 * 9);
return 1;
}
}
std::cout << std::endl << iter << " times sort done" << std::endl;
return 0;
}
/////ここまで検証コード列を順次実行
//テストドライバ
typedef int verifyFunction(void);
struct MyCexception : std::exception {
const char* what() const noexcept { return "C exception throwed\n"; }
} myCexception;
void myTerminate()
{
throw myCexception;
}
struct MyAbortException : std::exception {
const char* what() const noexcept { return "Abort signal Exception.\n"; }
} myAbortException;
void myAbortSignalHandler(int signum)
{
throw myAbortException;
}
struct MyIntSignalException : std::exception {
const char* what() const noexcept { return "Interrupted Signal Exception.\n"; }
} myIntSignalException;
void myIntSignalHandler(int signum)
{
throw myIntSignalException;
}
struct MySEVSignalException : std::exception {
const char* what() const noexcept { return "SIGSEGV 11 Signal Exception.\n"; }
} mySEVSignalException;
void mySEVSignalHandler(int signum)
{
throw mySEVSignalException;
}
struct MyFPESignalException : std::exception {
const char* what() const noexcept { return "SIGFPE Signal Exception.\n"; }
} myFPESignalException;
void myFPESignalHandler(int signum)
{
throw myFPESignalException;
}
class MyWorker {
verifyFunction *targetFunc;
char * testStatusDonePoint;
char * testStatus;
volatile bool running;
double elapsedTime;
int returnCode;
char resultMessage[4096];
public:
static int timeLimit; // sleep関数用に、整数でミリ秒単位の許容走行時間の上限
MyWorker(verifyFunction *targetFunc) :targetFunc(targetFunc) {
this->testStatusDonePoint = NULL;
this->testStatus = TestStatusStoreValue(a, __LINE__);
this->running = false;
this->elapsedTime = NAN;
this->returnCode = -1;
myStrCopy(this->resultMessage, "not begin.");
}
static void initTimeFacter();
void RunWithGuardedCPPException() {
this->testStatus = this->testStatusDonePoint = TestStatusStoreValue(b, __LINE__);
myStrCopy(this->resultMessage, "begin.");
this->running = true;
try {
std::chrono::high_resolution_clock::time_point startTime = std::chrono::high_resolution_clock::now();; // 計測スタート時刻
this->returnCode = this->targetFunc(); // テスト対象関数呼び出し
this->running = false;
std::chrono::high_resolution_clock::time_point endTime = std::chrono::high_resolution_clock::now();; // 計測終了時刻
std::chrono::duration<double> elapsed_seconds = std::chrono::duration_cast<std::chrono::duration<double>>(endTime - startTime);
this->elapsedTime = elapsed_seconds.count() ; //秒単位 浮動小数点数の走行時間計算
this->testStatus = this->testStatusDonePoint = TestStatusStoreValue(c, __LINE__);
}
catch (std::exception &ex) {
myStrCopy(this->resultMessage, ex.what());
}
catch (...) {
myStrCopy(this->resultMessage, "An exception was caught in catch (...).");
}
this->running = false;
}
void RunWithGuardedNonCPPException() {
std::terminate_handler unexpected_handlerBackup1 = std::set_terminate(myTerminate);
std::unexpected_handler unexpected_handlerBackup2 = std::set_unexpected(myTerminate);
std::signal(SIGINT, myIntSignalHandler);
std::signal(SIGABRT, myAbortSignalHandler);
std::signal(SIGSEGV, mySEVSignalHandler);
std::signal(SIGFPE, myFPESignalHandler);
#ifdef _MSC_VER
union uint32by4bit {
unsigned int fUint32;
struct {//エンディアン依存ビットフィールド
unsigned int f0 : 4;
unsigned int f1 : 4;
unsigned int f2 : 4;
unsigned int f3 : 4;
unsigned int f4 : 4;
unsigned int f5 : 4;
unsigned int f6 : 4;
unsigned int f7 : 4;
} xbit;
} exceptionCode;
_EXCEPTION_POINTERS *xp;
__try {
#endif
#ifdef _MSC_VER
_set_errno(0);
#else
errno = 0;
#endif
RunWithGuardedCPPException();
if (errno != 0) { // check MATH error
#pragma warning(disable : 4996)
myStrCopy(this->resultMessage, strerror(errno)); //warning C4996: 'strerror': This function or variable may be unsafe.
}
else if (this->returnCode == 0) {
myStrCopy(this->resultMessage, "Done & sucess.");
}
else {
myStrCopy(this->resultMessage, "Done & but Failed.");
}
#ifdef _MSC_VER
}
// __except will only catch an C-SEH- exception here
__except (exceptionCode.fUint32 = GetExceptionCode(),
xp = GetExceptionInformation(),
EXCEPTION_EXECUTE_HANDLER) {
// if the exception was not caught by the catch(...) inside fail()
#pragma warning(disable : 4996)
myStrCopy(this->resultMessage, "An exception was caught in __except ......... ");
#pragma warning(default : 4996)
resultMessage[43] = exceptionCode.xbit.f0 + (exceptionCode.xbit.f0 < 10 ? '0' : 'A' - 10);
resultMessage[42] = exceptionCode.xbit.f1 + (exceptionCode.xbit.f1 < 10 ? '0' : 'A' - 10);
resultMessage[41] = exceptionCode.xbit.f2 + (exceptionCode.xbit.f2 < 10 ? '0' : 'A' - 10);
resultMessage[40] = exceptionCode.xbit.f3 + (exceptionCode.xbit.f3 < 10 ? '0' : 'A' - 10);
resultMessage[39] = exceptionCode.xbit.f4 + (exceptionCode.xbit.f4 < 10 ? '0' : 'A' - 10);
resultMessage[38] = exceptionCode.xbit.f5 + (exceptionCode.xbit.f5 < 10 ? '0' : 'A' - 10);
resultMessage[37] = exceptionCode.xbit.f6 + (exceptionCode.xbit.f6 < 10 ? '0' : 'A' - 10);
resultMessage[36] = exceptionCode.xbit.f7 + (exceptionCode.xbit.f7 < 10 ? '0' : 'A' - 10);
//struct _EXCEPTION_RECORD *rec = xp->ExceptionRecord;
//std::stringstream ss;
//ss << std::hex
// << "An exception was caught in __except." << std::endl
// << " code:" << rec->ExceptionCode
// << " flags:" << rec->ExceptionFlags
// << " addr:" << rec->ExceptionAddress
// << " params:" << rec->NumberParameters
// << std::endl;
//return ss.str().c_str();
};
#endif
this->running = false;
};
void operator()() {
this->RunWithGuardedNonCPPException();
}
bool isRunning() {
return this->running;
}
static int getTimeLimit() {
return timeLimit;
}
double getElapsedTime() {
return this->elapsedTime;
}
char * getResultData() {
if (this->running) return "running yet.";
if (this->testStatus != this->testStatusDonePoint) {
myStrCat(resultMessage, "?!\n may be hacked return.");
}
return resultMessage;
}
};
int MyWorker::timeLimit; //ミリ秒 整数単位の許容走行時間の上限
void MyWorker::initTimeFacter() {
float testData[9] = { 2.0, 3.0, 5.0 , 7.0, 11.0,13.0, 17.0,19.0,23.0 };
std::chrono::high_resolution_clock::time_point startTime = std::chrono::high_resolution_clock::now(); // 高解像で計測スタート時刻
testByGenPerm<float *,float,void *>(9, (float *)testData,&sort9l);
std::chrono::high_resolution_clock::time_point endTime = std::chrono::high_resolution_clock::now();; // 計測終了時刻
std::chrono::duration<double> elapsed_seconds = std::chrono::duration_cast<std::chrono::duration<double>>(endTime - startTime);
MyWorker::timeLimit = (int)(elapsed_seconds.count() * 1000 * 2.0f);
std::cout << "参考処理の走行許容時間は " << elapsed_seconds.count() << "秒/test。" << std::endl;
std::cout << "このマシン環境での走行許容時間は " << MyWorker::timeLimit << "ミリ秒/test。" << std::endl;
}
class TestCaseClass {
verifyFunction *targetFunc;
char * title;
int timeOut;
char resultMessage[1024];
long elapsedTime;
public:
TestCaseClass(verifyFunction *pTargetFunc, char * pTitle, int pTimeOut)
:targetFunc(pTargetFunc), title(pTitle), timeOut(pTimeOut) {
}
void run() {
std::cout << (this->title) << ":" << std::endl;
MyWorker currentWorker(this->targetFunc);
std::thread targetThread(std::ref(currentWorker)); //currentWorker.RunWithGuardedNonCPPException();
#ifdef LINUXOS
usleep(this->timeOut * 1000);
#else
std::this_thread::sleep_for(std::chrono::milliseconds(this->timeOut));
#endif
if (currentWorker.isRunning()) {
targetThread.detach(); //テスト対象のスレッドの管理を外す。
myStrCopy(this->resultMessage, "time over.");
//テスト対象のネイティブ・スレッドは残存して走行し続けている。
}
else {
targetThread.join(); //テスト対象のスレッドの管理を終える。
myStrCopy(this->resultMessage, currentWorker.getResultData());
}
std::cout.setf(std::ios_base::fixed); std::cout.width(12); std::cout.precision(3);
std::cout << (currentWorker.getElapsedTime()) << "mSec. " << (this->resultMessage) << std::endl;
}
};
int main(int argc, char *argv[])
{
{
MyWorker::initTimeFacter();
}
//関数名:期待するexception,走行時間許容時間
{TestCaseClass(verifyCase1, "verifyCase1: integer zero divide", 16).run(); }
{TestCaseClass(verifyCase2, "verifyCase2: float zero divide or Nan", 16).run(); }
{TestCaseClass(verifyCase3, "verifyCase3: DOMAIN Error", 16).run(); }
{TestCaseClass(verifyCase4, "verifyCase4: stack over flow", 32).run(); }
{TestCaseClass(verifyCase5, "verifyCase5: vector subscript out of range", MyWorker::timeLimit).run(); }
{TestCaseClass(verifyCase6_NG, "verifyCase6_NG: hacking return ", MyWorker::timeLimit).run(); }
{TestCaseClass(verifyCase6_Ok, "verifyCase6_Ok: int sort normal ", MyWorker::timeLimit).run(); }
{TestCaseClass(verifyCase7, "verifyCase7: float sort normal ", MyWorker::timeLimit).run(); }
return 0;
}
/////ここまでテストドライバ
using namespace std;
/////非テストコードをココにincludeする
int testCase1() {//整数の0除算例外発生
cout << "\tin testCase1" << endl;
int i = 0, j = 1;
j /= i; // This will throw a SE (0xC0000094: Integer division by zero).
return 0;
}
int testCase2() {//浮動小数点数の0除算 NaN 発生
cout << "\tin testCase2" << endl;
double a = 0, b = 1;
#pragma warning(disable : 4723)
b = b / a; // This will throw a SE (divide by zero).
#pragma warning(default : 4723)
if (
#ifdef _MSC_VER
_isnan(b)
#else
std::isnan(b)
#endif
) {
return 0;
}
return 1; //通らない箇所
}
int testCase3() {// doubleのMatherr::_DOMAIN C言語がINFを返す仕様との非互換性の確認
cout << "\tin testCase3" << endl;
double x = 0.0 / log(0.0);
if (
#ifdef _MSC_VER
_isnan(x)
#else
std::isnan(x)
#endif
) {
return 0;
}
return 1;
}
int testCase4() {// stack over flow
cout << "\tin testCase4" << endl;
double a[512][512][512];
a[1023][1023][1023] = 1.0;
return 0;
}
//#undef _HAS_ITERATOR_DEBUGGING
//#define _SECURE_SCL 1
int testCase5() {// stack over flow
cout << "\tin testCase5" << endl;
std::vector<std::vector<std::vector<double>>> a;
a.resize(1024);
a[1023].resize(1024);
a[1023][1023].resize(1024);
a[1023][1023][1023] = 1.0;
a[1023][1023][2048] = 99; // Assart error "vector subscript out of range" --> Exceptionとして補足できない
return 0;
}
int testCase6_Ok(std::vector<int> & toBeSortedData) {//模範的な速度でintの整列が可能なことを例示するコード。 ただし、暴走しないでココまで到着する前提
return sort9l((int *)toBeSortedData.data());
}
int testCase6_NG(std::vector<int> & toBeSortedData) {// 整列結果の詐称 またはreturn先のハッキング。
#ifdef _MSC_VER
__asm {
push esi
push ebx
push ecx
push edx
}
#else
// __asm__ (
// "push %esi;"
// "push %ebx;"
// "push %ecx;"
// "push %edx;"
// ) ;
#endif
//memDump(toBeSortedData.data(), -64, 128);
std::sort(toBeSortedData.begin(),toBeSortedData.end(),std::greater<double>());//std::sortによる降順ソートでは遅すぎるのでハッキングしたくなる
static bool isFirstCall = true;
static std::vector<int> backupData;
if (isFirstCall) { //1回目の呼び出しがソートされている前提のハッキング
backupData = toBeSortedData;
isFirstCall = false;
}
else {
toBeSortedData = backupData;
}//これでも未だ遅いので、次は、アセンブラに挑戦したくなる
#ifdef _MSC_VER
//#pragma error(disable : 4235) Does not compile with Visual C++ /Arch:x64
#pragma warning(disable : 4731)
__asm { // フレーム ポインター レジスタ 'ebp' 変更
pop edx
pop ecx
pop ebx
pop esi
mov esp, ebp
pop ebp
xor eax, eax
mov esp, ebp
pop ebp
ret 0
} //STATUS_STACK_BUFFER_OVERRUN encounteredではなく、Verifyルーチンの代わりに正常なreturn 0に復帰させたいハッキングコード
#else
// __asm__ (
// "pop %edx;"
// "pop %ecx;"
// "pop %ebx;"
// "pop %esi;"
// );
// __asm__ (
// "mov %ebp,%esp;"
// "pop %ebp;"
// "mov %ebp,%esp;"
// "pop %ebp;"
// );
#endif
return 0;
}
int testCase7(std::vector<float> & toBeSortedData) {//模範的な速度でfloatの整列が可能なことを例示するコード。 ただし、暴走しないでココまで到着する前提
return sort9l((int *)toBeSortedData.data());
}
/////ここまで非テストコード