From fe52743b74fb7b7c8c1be95d06de1511bdbecdb1 Mon Sep 17 00:00:00 2001 From: Jocelyn Legault Date: Thu, 15 Oct 2009 22:28:36 -0600 Subject: [PATCH] Debug function guards: implement the guard. - Implement the guard. - Implement auxiliary "guard_output" macro that allow for properly indented comments. - Set of unit tests for the guard and for guard_output [#8 state:needs_ack] Signed-off-by: Jocelyn Legault --- PowerEditor/src/MISC/Common/Common.h | 4 +- PowerEditor/src/MISC/Debug/npp_debug.cpp | 88 +++++++-- PowerEditor/src/MISC/Debug/npp_debug.h | 50 +++++- PowerEditor/tests/testNppDebug.cpp | 169 ++++++++++++++++++ .../visual.net/notepadPlus.vc.8.0.vcproj | 4 + .../visual.net/notepadPlus.vc.9.0.vcproj | 4 + 6 files changed, 296 insertions(+), 23 deletions(-) create mode 100644 PowerEditor/tests/testNppDebug.cpp diff --git a/PowerEditor/src/MISC/Common/Common.h b/PowerEditor/src/MISC/Common/Common.h index a066fe88..b6b019be 100644 --- a/PowerEditor/src/MISC/Common/Common.h +++ b/PowerEditor/src/MISC/Common/Common.h @@ -131,10 +131,10 @@ class WcharMbcsConvertor #define ERROR_MSG_SIZE 1024 -#ifdef DEBUG +#ifdef _DEBUG #define NO_DEFAULT_CASE default: {\ TCHAR errorMsg[ERROR_MSG_SIZE];\ - sprintf_s(errorMsg, ERROR_MSG_SIZE, "Unhanded default case in %s, line %d", __FILE__, __LINE__ );\ + _stprintf_s(errorMsg, ERROR_MSG_SIZE, TEXT("Unhanded default case in %s, line %d"), __FILE__, __LINE__ );\ ::MessageBox(NULL, TEXT("Unhandled default case."), errorMsg, MB_OK|MB_ICONWARNING);\ }\ break diff --git a/PowerEditor/src/MISC/Debug/npp_debug.cpp b/PowerEditor/src/MISC/Debug/npp_debug.cpp index 05dce545..69f4aa37 100644 --- a/PowerEditor/src/MISC/Debug/npp_debug.cpp +++ b/PowerEditor/src/MISC/Debug/npp_debug.cpp @@ -21,21 +21,85 @@ #include "npp_debug.h" -namespace Debug +namespace NppDebug { - void OutputF(TCHAR* format, ...) + +static outputFunction debugOutput = OutputDebugString; + +outputFunction setOutputFunction(outputFunction newOutputFunction) +{ + outputFunction currentOutputFunc = debugOutput; + debugOutput = newOutputFunction; + return currentOutputFunc; +} + +void outputF(TCHAR* format, ...) +{ + va_list args; + int len; + TCHAR* buffer; + + va_start( args, format ); + len = _vsctprintf( format, args ) + 1; // _vscprintf doesn't count terminating '\0' + buffer = new TCHAR[len]; + _vstprintf_s( buffer, len, format, args ); + debugOutput(buffer); + delete [] buffer; +} + +int FuncGuard::_depth = 0; +TCHAR FuncGuard::_indent[MAX_DEBUG_INDENT]; + +FuncGuard::FuncGuard(const TCHAR* funcsig, const TCHAR* funcname, const TCHAR* file, int line): + _funcname(funcname) +{ + outputF(TEXT("%s%s(%d):\n"), getIndent(), file, line); + outputF(TEXT("%sEntering[ %s ]\n"), getIndent(), funcsig); + indent(); +} + +FuncGuard::~FuncGuard() +{ + unindent(); + outputF(TEXT("%sLeaving[ %s ]\n"), getIndent(), _funcname.c_str()); +} + +void FuncGuard::outputIndent() +{ + outputF(getIndent()); +} + +void FuncGuard::indent() +{ + _depth++; + if (_depth >= MAX_DEBUG_INDENT) + { + DebugBreak(); + _depth = MAX_DEBUG_INDENT-1; + } +} + +void FuncGuard::unindent() +{ + _depth--; + if (_depth < 0) + { + DebugBreak(); + _depth = 0; + } +} + +TCHAR* FuncGuard::getIndent() +{ + int i = 0;; + for (; i < _depth; i++) { - va_list args; - int len; - TCHAR* buffer; - - va_start( args, format ); - len = _vsctprintf( format, args ) + 1; // _vscprintf doesn't count terminating '\0' - buffer = new TCHAR[len]; - _vstprintf_s( buffer, len, format, args ); - OutputDebugString(buffer); - delete [] buffer; + _indent[i] = TEXT('\t'); } + _indent[i] = TEXT('\0'); + return _indent; +} + } // namespace Debug #endif // #ifndef SHIPPING diff --git a/PowerEditor/src/MISC/Debug/npp_debug.h b/PowerEditor/src/MISC/Debug/npp_debug.h index 5a60ea99..1b0a711b 100644 --- a/PowerEditor/src/MISC/Debug/npp_debug.h +++ b/PowerEditor/src/MISC/Debug/npp_debug.h @@ -19,16 +19,48 @@ #define NPP_DEBUG_H #ifndef SHIPPING - namespace Debug - { - void OutputF(TCHAR* format, ...); - } - - #define debugf(format, ...) Debug::OutputF(format, __VA_ARGS__) - #define _debugf(format, ...) Debug::OutputF(TEXT("%s(%d): "), TEXT(__FILE__), __LINE__); Debug::OutputF(format, __VA_ARGS__) -#else + +#define MAX_DEBUG_INDENT 1024 +namespace NppDebug +{ +typedef void (__stdcall *outputFunction)(const TCHAR*); + +outputFunction setOutputFunction(outputFunction newOutputFunction); + +void outputF(TCHAR* format, ...); + +// JOCE: Nice to have: configurable indent character (' ', '\t', '_', '+', etc...) +class FuncGuard +{ +public: + FuncGuard(const TCHAR* funcsig, const TCHAR* funcname, const TCHAR* file, int line); + ~FuncGuard(); + + void outputIndent(); + +private: + void indent(); + void unindent(); + TCHAR* getIndent(); + generic_string _funcname; + + static int _depth; + static TCHAR _indent[MAX_DEBUG_INDENT]; +}; + +} // namespace NppDebug + +#define debugf(format, ...) NppDebug::outputF(format, __VA_ARGS__) +// JOCE: modify to make that a single call to OutputF +#define line_debugf(format, ...) NppDebug::outputF(TEXT("%s(%d): "), TEXT(__FILE__), __LINE__); NppDebug::outputF(format, __VA_ARGS__) +// JOCE: modify to make that a single call to OutputF +#define guard_debugf(format, ...) __npp_func_guard__.outputIndent(); NppDebug::outputF(format, __VA_ARGS__) +#define func_guard() NppDebug::FuncGuard __npp_func_guard__(TEXT(__FUNCSIG__), TEXT(__FUNCTION__), TEXT(__FILE__), __LINE__) + +#else // if !SHIPPING #define debugf(format, ...) void(0) #define _debugf(format, ...) void(0) -#endif + #define guard_func() void(0) +#endif // SHIPPING #endif // NPP_DEBUG_H diff --git a/PowerEditor/tests/testNppDebug.cpp b/PowerEditor/tests/testNppDebug.cpp new file mode 100644 index 00000000..b49a4ca9 --- /dev/null +++ b/PowerEditor/tests/testNppDebug.cpp @@ -0,0 +1,169 @@ +// This file is part of notepad++ +// Copyright (C)2009 The Notepad++ Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#include "precompiled_headers.h" + +#ifndef SHIPPING + +// JOCE tests would be needed for debugf and _debugf as well. + +////////////////////////////////////////////////////////////////////////// +// +// Table of Content: +// - FuncGuardTest +// +////////////////////////////////////////////////////////////////////////// + + + +////////////////////////////////////////////////////////////////////////// +// +// FuncGuardTest +// +////////////////////////////////////////////////////////////////////////// + +static generic_string s_outputResult; + +static void __stdcall testOutputFunction(const TCHAR* input) +{ + s_outputResult.append(input); +} + +class FuncGuardTest : public ::testing::Test +{ +public: + FuncGuardTest() : + originalOutput(NppDebug::setOutputFunction(testOutputFunction)) + { + s_outputResult = TEXT(""); + } + + ~FuncGuardTest() + { + NppDebug::setOutputFunction(originalOutput); + } + + NppDebug::outputFunction originalOutput; +}; + + +// JOCE: If we could make the "expected" string looking more like what the actual output is, that'd be nice. +TEST_F(FuncGuardTest, guardSimple) +{ + { + NppDebug::FuncGuard guard(TEXT("sig"), TEXT("name"), TEXT("file"), 1); + ASSERT_EQ(TEXT("file(1):\nEntering[ sig ]\n"), s_outputResult); + } + ASSERT_EQ(TEXT("file(1):\nEntering[ sig ]\nLeaving[ name ]\n"), s_outputResult); +} + +TEST_F(FuncGuardTest, guardSimpleIndent) +{ + { + NppDebug::FuncGuard guard1(TEXT("sig"), TEXT("name"), TEXT("file"), 1); + ASSERT_EQ(TEXT("file(1):\nEntering[ sig ]\n"), s_outputResult); + { + NppDebug::FuncGuard guard2(TEXT("sig2"), TEXT("name2"), TEXT("file2"), 2); + ASSERT_EQ(TEXT("file(1):\nEntering[ sig ]\n\tfile2(2):\n\tEntering[ sig2 ]\n"), s_outputResult); + } + ASSERT_EQ(TEXT("file(1):\nEntering[ sig ]\n\tfile2(2):\n\tEntering[ sig2 ]\n\tLeaving[ name2 ]\n"), s_outputResult); + } + ASSERT_EQ(TEXT("file(1):\nEntering[ sig ]\n\tfile2(2):\n\tEntering[ sig2 ]\n\tLeaving[ name2 ]\nLeaving[ name ]\n"), s_outputResult); +} + +static TCHAR TestGuardFuncOneGuardLine[8]; +void TestGuardFuncOne() +{ + func_guard(); int line = __LINE__; // This declaration needs to be on the same line as func_guard to get the right line number. + _itot_s(line, TestGuardFuncOneGuardLine, 10); +} + +static TCHAR TestGuardFuncTwoGuardLine[8]; +void TestGuardFuncTwo() +{ + func_guard(); int line = __LINE__; // This declaration needs to be on the same line as func_guard to get the right line number. + _itot_s(line, TestGuardFuncTwoGuardLine, 10); + TestGuardFuncOne(); +} + +TEST_F(FuncGuardTest, guardFunc) +{ + TestGuardFuncOne(); + generic_string expected = TEXT(__FILE__); + expected += TEXT('('); + expected += TestGuardFuncOneGuardLine; + expected += TEXT("):\n"); + expected += TEXT("Entering[ void __cdecl TestGuardFuncOne(void) ]\n"); + expected += TEXT("Leaving[ TestGuardFuncOne ]\n"); + ASSERT_EQ(expected, s_outputResult); +} + +TEST_F(FuncGuardTest, guardFuncIndent) +{ + TestGuardFuncTwo(); + generic_string expected = TEXT(__FILE__); + expected += TEXT('('); + expected += TestGuardFuncTwoGuardLine; + expected += TEXT("):\n"); + expected += TEXT("Entering[ void __cdecl TestGuardFuncTwo(void) ]\n"); + expected += TEXT("\t"); + expected += TEXT(__FILE__); + expected += TEXT('('); + expected += TestGuardFuncOneGuardLine; + expected += TEXT("):\n"); + expected += TEXT("\tEntering[ void __cdecl TestGuardFuncOne(void) ]\n"); + expected += TEXT("\tLeaving[ TestGuardFuncOne ]\n"); + expected += TEXT("Leaving[ TestGuardFuncTwo ]\n"); + ASSERT_EQ(expected, s_outputResult); +} + +void TestGuardFuncWithCommentOne() +{ + func_guard(); int line = __LINE__; // This declaration needs to be on the same line as func_guard to get the right line number. + _itot_s(line, TestGuardFuncOneGuardLine, 10); + guard_debugf(TEXT("We print a number: %d\n"), 42); +} + +void TestGuardFuncWithCommentTwo() +{ + func_guard(); int line = __LINE__; // This declaration needs to be on the same line as func_guard to get the right line number. + _itot_s(line, TestGuardFuncTwoGuardLine, 10); + TestGuardFuncWithCommentOne(); +} + +TEST_F(FuncGuardTest, guardFuncIndentWithDebugComment) +{ + TestGuardFuncWithCommentTwo(); + generic_string expected = TEXT(__FILE__); + expected += TEXT('('); + expected += TestGuardFuncTwoGuardLine; + expected += TEXT("):\n"); + expected += TEXT("Entering[ void __cdecl TestGuardFuncWithCommentTwo(void) ]\n"); + expected += TEXT("\t"); + expected += TEXT(__FILE__); + expected += TEXT('('); + expected += TestGuardFuncOneGuardLine; + expected += TEXT("):\n"); + expected += TEXT("\tEntering[ void __cdecl TestGuardFuncWithCommentOne(void) ]\n"); + expected += TEXT("\t\tWe print a number: 42\n"); + expected += TEXT("\tLeaving[ TestGuardFuncWithCommentOne ]\n"); + expected += TEXT("Leaving[ TestGuardFuncWithCommentTwo ]\n"); + ASSERT_EQ(expected, s_outputResult); +} + + +#endif // SHIPPING \ No newline at end of file diff --git a/PowerEditor/visual.net/notepadPlus.vc.8.0.vcproj b/PowerEditor/visual.net/notepadPlus.vc.8.0.vcproj index 8ae41636..0a6b8cac 100644 --- a/PowerEditor/visual.net/notepadPlus.vc.8.0.vcproj +++ b/PowerEditor/visual.net/notepadPlus.vc.8.0.vcproj @@ -1729,6 +1729,10 @@ RelativePath="..\tests\testCommon.cpp" > + + diff --git a/PowerEditor/visual.net/notepadPlus.vc.9.0.vcproj b/PowerEditor/visual.net/notepadPlus.vc.9.0.vcproj index b88b5c8a..ab7bbd3c 100644 --- a/PowerEditor/visual.net/notepadPlus.vc.9.0.vcproj +++ b/PowerEditor/visual.net/notepadPlus.vc.9.0.vcproj @@ -752,6 +752,10 @@ RelativePath="..\tests\testCommon.cpp" > + +