Veteran Member
Join Date: Aug 2015
Location: Dreams, zz
|
12-27-2016
, 04:06
General Numeric Base Conversion - Any base to any base
|
#1
|
General Numeric Base Conversion - Any base to any base
References: - Wikipedia - Radix
- Wikipedia - Positional notation
- Wikipedia - Positional notation
- List of numeral systems
- Phanderson - Base Conversion
- StackExchange Computer Science - The math behind converting from any base to any base without going through base 10?
The stock:
Code:
/**
* Given a number on a certain base until 10, calculates and return the equivalent number on another
* base until 10.
*
* @param origin_number the number to be converted.
* @param origin_base the base where `origin_number` is on.
* @param destiny_base the base where `origin_number` is to be converted to.
*/
stock convert_numeric_base( origin_number, origin_base, destiny_base )
{
new integer;
new Array:digits;
digits = toDigitsRepresentation( origin_number, 10 );
integer = fromDigitsRepresentation( digits, origin_base );
ArrayDestroy( digits );
digits = toDigitsRepresentation( integer, destiny_base );
integer = fromDigitsRepresentation( digits, 10 );
ArrayDestroy( digits );
return integer;
}
/**
* Read an digits Dynamic Array at its given base and return an integer representation.
*
* @param digits a Dynamic Array within the digits of inputed number on the specified base.
* @param origin_base the base where `digits` are represented.
*
* return an integer representation inputed number on the specified base.
*/
stock fromDigitsRepresentation( Array:digits, origin_base )
{
new integer;
new arraySize = ArraySize( digits );
for( new index = 0; index < arraySize; index ++ )
{
integer = integer * origin_base + ArrayGetCell( digits, index );
}
return integer;
}
/**
* Create an Dynamic Array within of the decimal number at its given base.
*
* @param origin_number a decimal number to be converted.
* @param origin_base the base where `origin_number` is to be converted to.
*
* return a Dynamic Array within the digits of inputed number on the specified base.
*/
stock Array:toDigitsRepresentation( origin_number, origin_base )
{
new Array:digits = ArrayCreate();
ArrayPushCell( digits, origin_number % origin_base );
origin_number = origin_number / origin_base;
while( origin_number > 0 )
{
ArrayInsertCellBefore( digits, 0, origin_number % origin_base );
origin_number = origin_number / origin_base;
}
return digits;
}
The Unit Tests Results:
HTML Code:
L 12/29/2016 - 16:59:05: {1.000 16292 -1944939 -1944939} I AM ENTERING ON configureTheUnitTests(0)
L 12/29/2016 - 16:59:05: {1.000 16296 -1944936 3}
L 12/29/2016 - 16:59:05: {1.000 16296 -1944919 17}
L 12/29/2016 - 16:59:05: {1.000 16296 -1944914 5}
L 12/29/2016 - 16:59:05: {1.000 16296 -1944910 4}
L 12/29/2016 - 16:59:05: {1.000 16296 -1944904 6}
L 12/29/2016 - 16:59:05: {1.000 14928 -1944899 5} EXECUTING TEST 1 AFTER 1 SECONDS - test_convertNumericBase.aa_case1
L 12/29/2016 - 16:59:05: {1.000 15196 -1944889 10} I AM ENTERING ON convert_numeric_base(3) | number: 10 (7->10)
L 12/29/2016 - 16:59:05: {1.000 14692 -1944886 3} Array Cells: (0) 1, (1) 0,
L 12/29/2016 - 16:59:05: {1.000 14692 -1944882 4} Array Cells: (0) 7,
L 12/29/2016 - 16:59:05: {1.000 15196 -1944878 4} ( convert_numeric_base ) Returning integer: 7
L 12/29/2016 - 16:59:05: {1.000 14904 -1944872 6} ( displaysLastTestOk ) numberOfFailures: 0, lastFailure: 0, lastTestId: 1
L 12/29/2016 - 16:59:05: {1.000 14920 -1944868 4} OK!
L 12/29/2016 - 16:59:05: {1.000 14920 -1944863 5}
L 12/29/2016 - 16:59:05: {1.000 14920 -1944859 4}
L 12/29/2016 - 16:59:05: {1.000 14928 -1944853 6} EXECUTING TEST 2 AFTER 1 SECONDS - test_convertNumericBase.aa_case2
L 12/29/2016 - 16:59:05: {1.000 15196 -1944849 4} I AM ENTERING ON convert_numeric_base(3) | number: 10 (6->10)
L 12/29/2016 - 16:59:05: {1.000 14692 -1944845 4} Array Cells: (0) 1, (1) 0,
L 12/29/2016 - 16:59:05: {1.000 14692 -1944841 4} Array Cells: (0) 6,
L 12/29/2016 - 16:59:05: {1.000 15196 -1944836 5} ( convert_numeric_base ) Returning integer: 6
L 12/29/2016 - 16:59:05: {1.000 14904 -1944832 4} ( displaysLastTestOk ) numberOfFailures: 0, lastFailure: 0, lastTestId: 2
L 12/29/2016 - 16:59:05: {1.000 14920 -1944828 4} OK!
L 12/29/2016 - 16:59:05: {1.000 14920 -1944822 6}
L 12/29/2016 - 16:59:05: {1.000 14920 -1944816 6}
L 12/29/2016 - 16:59:05: {1.000 14928 -1944812 4} EXECUTING TEST 3 AFTER 1 SECONDS - test_convertNumericBase.aa_case3
L 12/29/2016 - 16:59:05: {1.000 15196 -1944808 4} I AM ENTERING ON convert_numeric_base(3) | number: 10 (5->10)
L 12/29/2016 - 16:59:05: {1.000 14692 -1944803 5} Array Cells: (0) 1, (1) 0,
L 12/29/2016 - 16:59:06: {1.000 14692 -1944798 5} Array Cells: (0) 5,
L 12/29/2016 - 16:59:06: {1.000 15196 -1944794 4} ( convert_numeric_base ) Returning integer: 5
L 12/29/2016 - 16:59:06: {1.000 14904 -1944790 4} ( displaysLastTestOk ) numberOfFailures: 0, lastFailure: 0, lastTestId: 3
L 12/29/2016 - 16:59:06: {1.000 14920 -1944784 6} OK!
L 12/29/2016 - 16:59:06: {1.000 14920 -1944779 5}
L 12/29/2016 - 16:59:06: {1.000 14920 -1944776 3}
L 12/29/2016 - 16:59:06: {1.000 14928 -1944769 7} EXECUTING TEST 4 AFTER 2 SECONDS - test_convertNumericBase.aa_case4
L 12/29/2016 - 16:59:06: {1.000 15196 -1944765 4} I AM ENTERING ON convert_numeric_base(3) | number: 10 (8->10)
L 12/29/2016 - 16:59:06: {1.000 14692 -1944761 4} Array Cells: (0) 1, (1) 0,
L 12/29/2016 - 16:59:06: {1.000 14692 -1944758 3} Array Cells: (0) 8,
L 12/29/2016 - 16:59:06: {1.000 15196 -1944751 7} ( convert_numeric_base ) Returning integer: 8
L 12/29/2016 - 16:59:06: {1.000 14904 -1944748 3} ( displaysLastTestOk ) numberOfFailures: 0, lastFailure: 0, lastTestId: 4
L 12/29/2016 - 16:59:06: {1.000 14920 -1944744 4} OK!
L 12/29/2016 - 16:59:06: {1.000 14920 -1944736 8}
L 12/29/2016 - 16:59:06: {1.000 14920 -1944732 4}
L 12/29/2016 - 16:59:06: {1.000 14928 -1944730 2} EXECUTING TEST 5 AFTER 2 SECONDS - test_convertNumericBase.aa_case5
L 12/29/2016 - 16:59:06: {1.000 15196 -1944726 4} I AM ENTERING ON convert_numeric_base(3) | number: 10 (9->10)
L 12/29/2016 - 16:59:06: {1.000 14692 -1944719 7} Array Cells: (0) 1, (1) 0,
L 12/29/2016 - 16:59:06: {1.000 14692 -1944716 3} Array Cells: (0) 9,
L 12/29/2016 - 16:59:06: {1.000 15196 -1944712 4} ( convert_numeric_base ) Returning integer: 9
L 12/29/2016 - 16:59:06: {1.000 14904 -1944708 4} ( displaysLastTestOk ) numberOfFailures: 0, lastFailure: 0, lastTestId: 5
L 12/29/2016 - 16:59:06: {1.000 14920 -1944702 6} OK!
L 12/29/2016 - 16:59:06: {1.000 14920 -1944698 4}
L 12/29/2016 - 16:59:06: {1.000 14920 -1944694 4}
L 12/29/2016 - 16:59:06: {1.000 14928 -1944689 5} EXECUTING TEST 6 AFTER 2 SECONDS - test_convertNumericBase.aa_case6
L 12/29/2016 - 16:59:06: {1.000 15196 -1944684 5} I AM ENTERING ON convert_numeric_base(3) | number: 10 (10->10)
L 12/29/2016 - 16:59:06: {1.000 14692 -1944681 3} Array Cells: (0) 1, (1) 0,
L 12/29/2016 - 16:59:06: {1.000 14692 -1944677 4} Array Cells: (0) 1, (1) 0,
L 12/29/2016 - 16:59:06: {1.000 15196 -1944671 6} ( convert_numeric_base ) Returning integer: 10
L 12/29/2016 - 16:59:06: {1.000 14904 -1944667 4} ( displaysLastTestOk ) numberOfFailures: 0, lastFailure: 0, lastTestId: 6
L 12/29/2016 - 16:59:06: {1.000 14920 -1944663 4} OK!
L 12/29/2016 - 16:59:06: {1.000 14920 -1944659 4}
L 12/29/2016 - 16:59:06: {1.000 14920 -1944653 6}
L 12/29/2016 - 16:59:06: {1.000 14928 -1944649 4} EXECUTING TEST 7 AFTER 2 SECONDS - test_convertNumericBase.aa_case7
L 12/29/2016 - 16:59:06: {1.000 15196 -1944645 4} I AM ENTERING ON convert_numeric_base(3) | number: 11 (7->9)
L 12/29/2016 - 16:59:06: {1.000 14692 -1944641 4} Array Cells: (0) 1, (1) 1,
L 12/29/2016 - 16:59:06: {1.000 14692 -1944636 5} Array Cells: (0) 8,
L 12/29/2016 - 16:59:06: {1.000 15196 -1944632 4} ( convert_numeric_base ) Returning integer: 8
L 12/29/2016 - 16:59:06: {1.000 14904 -1944628 4} ( displaysLastTestOk ) numberOfFailures: 0, lastFailure: 0, lastTestId: 7
L 12/29/2016 - 16:59:06: {1.000 14920 -1944622 6} OK!
L 12/29/2016 - 16:59:06: {1.000 14920 -1944618 4}
L 12/29/2016 - 16:59:06: {1.000 14920 -1944614 4}
L 12/29/2016 - 16:59:06: {1.000 14928 -1944610 4} EXECUTING TEST 8 AFTER 2 SECONDS - test_convertNumericBase.aa_case8
L 12/29/2016 - 16:59:06: {1.000 15196 -1944608 2} I AM ENTERING ON convert_numeric_base(3) | number: 2462 (7->9)
L 12/29/2016 - 16:59:06: {1.000 14668 -1944601 7} Array Cells: (0) 2, (1) 4, (2) 6, (3) 2,
L 12/29/2016 - 16:59:06: {1.000 14668 -1944597 4} Array Cells: (0) 1, (1) 2, (2) 3, (3) 8,
L 12/29/2016 - 16:59:06: {1.000 15196 -1944593 4} ( convert_numeric_base ) Returning integer: 1238
L 12/29/2016 - 16:59:06: {1.000 14904 -1944586 7} ( displaysLastTestOk ) numberOfFailures: 0, lastFailure: 0, lastTestId: 8
L 12/29/2016 - 16:59:06: {1.000 14920 -1944583 3} OK!
L 12/29/2016 - 16:59:06: {1.000 14920 -1944579 4}
L 12/29/2016 - 16:59:06: {1.000 14920 -1944575 4}
L 12/29/2016 - 16:59:06: {1.000 14928 -1944569 6} EXECUTING TEST 9 AFTER 2 SECONDS - test_convertNumericBase.aa_case9
L 12/29/2016 - 16:59:06: {1.000 15196 -1944565 4} I AM ENTERING ON convert_numeric_base(3) | number: 1238 (9->7)
L 12/29/2016 - 16:59:06: {1.000 14668 -1944561 4} Array Cells: (0) 1, (1) 2, (2) 3, (3) 8,
L 12/29/2016 - 16:59:06: {1.000 14668 -1944556 5} Array Cells: (0) 2, (1) 4, (2) 6, (3) 2,
L 12/29/2016 - 16:59:06: {1.000 15196 -1944551 5} ( convert_numeric_base ) Returning integer: 2462
L 12/29/2016 - 16:59:06: {1.000 16256 -1944548 3} ( displaysLastTestOk ) numberOfFailures: 0, lastFailure: 0, lastTestId: 9
L 12/29/2016 - 16:59:06: {1.000 16272 -1944544 4} OK!
L 12/29/2016 - 16:59:06: {1.000 16272 -1944538 6}
L 12/29/2016 - 16:59:06: {1.000 16272 -1944534 4}
L 12/29/2016 - 16:59:06: {1.000 16296 -1944530 4}
L 12/29/2016 - 16:59:06: {1.000 16280 -1944526 4} I AM ENTERING ON print_all_tests_executed(0)
L 12/29/2016 - 16:59:06: {1.000 16024 -1944520 6}
L 12/29/2016 - 16:59:06: {1.000 16024 -1944517 3}
L 12/29/2016 - 16:59:06: {1.000 16024 -1944512 5}
L 12/29/2016 - 16:59:06: {1.000 16024 -1944509 3} The following tests were executed:
L 12/29/2016 - 16:59:06: {1.000 16024 -1944503 6}
L 12/29/2016 - 16:59:06: {1.000 16008 -1944498 5} 1. test_convertNumericBase.aa_case1
L 12/29/2016 - 16:59:06: {1.000 16008 -1944494 4} 2. test_convertNumericBase.aa_case2
L 12/29/2016 - 16:59:06: {1.000 16008 -1944489 5} 3. test_convertNumericBase.aa_case3
L 12/29/2016 - 16:59:06: {1.000 16008 -1944485 4} 4. test_convertNumericBase.aa_case4
L 12/29/2016 - 16:59:06: {1.000 16008 -1944484 1} 5. test_convertNumericBase.aa_case5
L 12/29/2016 - 16:59:06: {1.000 16008 -1944476 8} 6. test_convertNumericBase.aa_case6
L 12/29/2016 - 16:59:06: {1.000 16008 -1944470 6} 7. test_convertNumericBase.aa_case7
L 12/29/2016 - 16:59:06: {1.000 16008 -1944466 4} 8. test_convertNumericBase.aa_case8
L 12/29/2016 - 16:59:06: {1.000 16008 -1944462 4} 9. test_convertNumericBase.aa_case9
L 12/29/2016 - 16:59:06: {1.000 14996 -1944458 4}
L 12/29/2016 - 16:59:06: {1.000 14988 -1944453 5} 9 tests succeed.
L 12/29/2016 - 16:59:06: {1.000 14992 -1944449 4} 0 tests failed.
L 12/29/2016 - 16:59:06: {1.000 16284 -1944445 4}
L 12/29/2016 - 16:59:06: {1.000 16272 -1944439 6} Finished the General Numeric Base Conversion's Unit Tests execution after '2' seconds.
L 12/29/2016 - 16:59:06: {1.000 16284 -1944435 4}
L 12/29/2016 - 16:59:06: {1.000 16284 -1944431 4}
The Unit Tests Source Code:
Spoiler
PHP Code:
/** AMX Mod X Script * * 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, see <http://www.gnu.org/licenses/>. * * ***********************************************************
*/
#include <amxmodx>
// ################################# Unit Tests Header Start ################################## #define MAX_SHORT_STRING 64 #define MAX_LONG_STRING 256 #define MAX_BIG_BOSS_STRING 512
#define LOGGER(%1) debugMesssageLogger( %1 );
/** * Call the internal function to perform its task and stop the current test execution to avoid * double failure at the test control system. * * @see the stock 'setTestFailure(3)'. */ #define SET_TEST_FAILURE(%1) \ { \ if( setTestFailure( %1 ) ) \ { \ LOGGER( 1, " ( SET_TEST_FAILURE ) Just returning/bloking." ) \ return; \ } \ }
/** * General handler to assist object property applying and keep the code clear. This only need * to be used with destructors/cleaners which does not support uninitialized handlers, requiring * an if pre-checking. * * @param objectHandler the object handler to be called. * @param objectIndentifation the object identification number to be destroyed. */ #define TRY_TO_APPLY(%1,%2) \ { \ LOGGER( 128, "I AM ENTERING ON TRY_TO_APPLY(2) | objectIndentifation: %d", %2 ) \ if( %2 ) \ { \ %1( %2 ); \ } \ }
new g_test_testsNumber; new g_debug_level = 255; new g_test_lastTimeStamp; new g_test_startDayInteger;
/** * Used to know when the Unit Tests are running. */ new bool:g_test_isTheUnitTestsRunning;
/** * General data. */ new const PLUGIN_NAME[] = "General Numeric Base Conversion"; new const DEBUGGER_OUTPUT_LOG_FILE_NAME[] = "text.log";
/** * Test unit variables related to the DEBUG_LEVEL_UNIT_TESTs'. */ new g_test_failureNumber;
new Trie: g_test_failureIdsTrie; new Array:g_test_failureIdsArray; new Array:g_test_idsAndNamesArray; new Array:g_test_failureReasonsArray; // ################################# Unit Tests Header End ####################################
public plugin_init() { register_plugin( PLUGIN_NAME, "1.0", "Addons zz" );
server_print( "^n^n^n^n^n^n^n^n^n^n^n^n" ) configureTheUnitTests()
test_convertNumericBase_load() printTheUnitTestsResults() }
/** * To test the stock convert_numeric_base(3). */ stock test_convertNumericBase_load() { test_convertNumericBase( .origin_number = 10 , .origin_base = 7 , .destiny_base = 10, .expected = 7 ); // Case 1 test_convertNumericBase( .origin_number = 10 , .origin_base = 6 , .destiny_base = 10, .expected = 6 ); // Case 2 test_convertNumericBase( .origin_number = 10 , .origin_base = 5 , .destiny_base = 10, .expected = 5 ); // Case 3 test_convertNumericBase( .origin_number = 10 , .origin_base = 8 , .destiny_base = 10, .expected = 8 ); // Case 4 test_convertNumericBase( .origin_number = 10 , .origin_base = 9 , .destiny_base = 10, .expected = 9 ); // Case 5 test_convertNumericBase( .origin_number = 10 , .origin_base = 10, .destiny_base = 10, .expected = 10 ); // Case 6 test_convertNumericBase( .origin_number = 11 , .origin_base = 7 , .destiny_base = 9 , .expected = 8 ); // Case 7 test_convertNumericBase( .origin_number = 2462, .origin_base = 7 , .destiny_base = 9 , .expected = 1238 ); // Case 8 test_convertNumericBase( .origin_number = 1238, .origin_base = 9 , .destiny_base = 7 , .expected = 2462 ); // Case 9 }
/** * Create one case test for the stock convert_numeric_base(0) based on its parameters passed * by the test_convertNumericBase_load(0) loader function. */ stock test_convertNumericBase( origin_number, origin_base, destiny_base, expected ) { new errorMessage[ MAX_LONG_STRING ]; new test_id = test_registerSeriesNaming( "test_convertNumericBase", 'a' );
new result = convert_numeric_base( origin_number, origin_base, destiny_base );
formatex( errorMessage, charsmax( errorMessage ), "Converting the number %d on base %d to base %d must to be %d, instead of %d.", origin_number, origin_base, destiny_base, expected, result );
SET_TEST_FAILURE( test_id, result != expected, errorMessage ) }
/** * Given a number on a certain base until 10, calculates and return the equivalent number on another * base until 10. * * @param origin_number the number to be converted. * @param origin_base the base where `origin_number` is on. * @param destiny_base the base where `origin_number` is to be converted to. */ stock convert_numeric_base( origin_number, origin_base, destiny_base ) { LOGGER( 128, "I AM ENTERING ON convert_numeric_base(3) | number: %d (%d->%d)", \ origin_number, origin_base, destiny_base )
new integer; new Array:digits;
digits = toDigitsRepresentation( origin_number, 10 ); printDynamicArrayCells( digits );
integer = fromDigitsRepresentation( digits, origin_base ); ArrayDestroy( digits );
digits = toDigitsRepresentation( integer, destiny_base ); printDynamicArrayCells( digits );
integer = fromDigitsRepresentation( digits, 10 ); ArrayDestroy( digits );
LOGGER( 1, " ( convert_numeric_base ) Returning integer: %d", integer ) return integer; }
/** * Read an digits Dynamic Array at its given base and return an integer representation. * * @param digits a Dynamic Array within the digits of inputed number on the specified base. * @param origin_base the base where `digits` are represented. * * return an integer representation inputed number on the specified base. */ stock fromDigitsRepresentation( Array:digits, origin_base ) { new integer; new arraySize = ArraySize( digits );
for( new index = 0; index < arraySize; index ++ ) { integer = integer * origin_base + ArrayGetCell( digits, index ); }
return integer; }
/** * Create an Dynamic Array within of the decimal number at its given base. * * @param origin_number a decimal number to be converted. * @param origin_base the base where `origin_number` is to be converted to. * * return a Dynamic Array within the digits of inputed number on the specified base. */ stock Array:toDigitsRepresentation( origin_number, origin_base ) { new Array:digits = ArrayCreate();
ArrayPushCell( digits, origin_number % origin_base ); origin_number = origin_number / origin_base;
while( origin_number > 0 ) { ArrayInsertCellBefore( digits, 0, origin_number % origin_base ); origin_number = origin_number / origin_base; }
return digits; }
// ################################# Unit Tests Body Start #################################### #define MAX_CELL_LENGHT 20 #define MAX_MESSAGE_LENGHT 80
stock printDynamicArrayCells( Array:array ) { // server_print( "" );
new indexString [ 8 ]; new elementString [ 8 ]; new cellString [ MAX_CELL_LENGHT ]; new cellStringsBuffer[ MAX_MESSAGE_LENGHT ];
new arraySize = ArraySize( array );
for( new index = 0; index < arraySize; index++ ) { // Create a cellString within a trailing comma formatex( indexString , charsmax( indexString ) , "(%d) " , index ); formatex( elementString, charsmax( elementString ), "%d, " , ArrayGetCell( array, index ) ); formatex( cellString , charsmax( cellString ) , "%7s %-9s" , indexString, elementString );
// Add a new cellString to the output buffer and flushes it when it full announceCellString( cellString, cellStringsBuffer ); }
// Flush the last cells flushCellStrings( cellStringsBuffer ); // server_print( "" ); }
/** * Announce cells to the server console. * * @param cellStringAnnounce a cell as string. * @param cellStringsBuffer the output string to be printed. * * @note It does not immediately print the cell, the output occurs when the buffer is full. */ stock announceCellString( cellStringAnnounce[], cellStringsBuffer[] ) { static copiedChars;
// Reset the characters counter for the output flush if( !cellStringsBuffer[ 0 ] ) { copiedChars = 0; }
// Add the cellString to the buffer copiedChars += copy( cellStringsBuffer[ copiedChars ], MAX_MESSAGE_LENGHT - 1 - copiedChars, cellStringAnnounce );
// Calculate whether to flush now or not if( copiedChars > MAX_MESSAGE_LENGHT - MAX_CELL_LENGHT ) { flushCellStrings( cellStringsBuffer ); } }
/** * Print the current buffer, if there are any cellStrings on it. * * @param cellStringsBuffer the formatted cellStrings list to be printed. */ stock flushCellStrings( cellStringsBuffer[] ) { if( cellStringsBuffer[ 0 ] ) { // Print the message LOGGER( 1, "%-13s%s", "Array Cells: ", cellStringsBuffer[ 0 ] )
// Clear the buffer cellStringsBuffer[ 0 ] = '^0'; } }
/** * Write debug messages accordantly with the 'g_debug_level' variable. * * @param mode the debug mode level, see the variable 'g_debug_level' for the levels. * @param text the debug message, if omitted its default value is "" * @param any the variable number of formatting parameters * * @see the stock writeToTheDebugFile( log_file[], formated_message[] ) for the output log * 'DEBUGGER_OUTPUT_LOG_FILE_NAME'. */ stock debugMesssageLogger( const mode, const message[] = "", any:... ) { if( mode & g_debug_level ) { static formated_message[ MAX_BIG_BOSS_STRING ]; vformat( formated_message, charsmax( formated_message ), message, 3 );
writeToTheDebugFile( DEBUGGER_OUTPUT_LOG_FILE_NAME, formated_message ); } }
/** * Write debug messages to server's console and log file. * * @param message the debug message, if omitted its default value is "" * @param any the variable number of formatting parameters * * @see the stock writeToTheDebugFile( log_file[], formated_message[] ) for the output log * 'DEBUGGER_OUTPUT_LOG_FILE_NAME'. */ stock print_logger( const message[] = "", any:... ) { static formated_message[ MAX_BIG_BOSS_STRING ]; vformat( formated_message, charsmax( formated_message ), message, 2 );
writeToTheDebugFile( DEBUGGER_OUTPUT_LOG_FILE_NAME, formated_message ); }
/** * Write messages to the debug log file on 'addons/amxmodx/logs'. * * @param log_file the log file name. * @param formated_message the formatted message to write down to the debug log file. */ stock writeToTheDebugFile( const log_file[], const formated_message[] ) { new currentTime; static lastRun;
currentTime = tickcount();
log_to_file( log_file, "{%.3f %d %5d %4d} %s", get_gametime(), heapspace(), currentTime, currentTime - lastRun, formated_message ); lastRun = currentTime;
// Removes the compiler warning `warning 203: symbol is never used` when only the debug // level `DEBUG_LEVEL_NORMAL` is enabled. if( g_test_isTheUnitTestsRunning ) { } }
stock displaysLastTestOk() { if( g_test_testsNumber > 0 ) { new lastTestId = ( g_test_testsNumber ); new numberOfFailures = ArraySize( g_test_failureIdsArray ); new lastFailure = ( numberOfFailures? ArrayGetCell( g_test_failureIdsArray, numberOfFailures - 1 ) : 0 );
LOGGER( 1, "( displaysLastTestOk ) numberOfFailures: %d, lastFailure: %d, lastTestId: %d", \ numberOfFailures, lastFailure, lastTestId )
if( !numberOfFailures || lastFailure != lastTestId ) { print_logger( "OK!" ); print_logger( "" ); print_logger( "" ); } else if( lastFailure == lastTestId ) { print_logger( "FAILED!" ); print_logger( "" ); print_logger( "" ); print_logger( "" );
// Blocks the delayed Unit Tests to run, because the chain is broke. return false; } }
return true; }
stock print_all_tests_executed() { LOGGER( 128, "I AM ENTERING ON print_all_tests_executed(0)" )
new test_name[ MAX_SHORT_STRING ]; new testsNumber = ArraySize( g_test_idsAndNamesArray );
print_logger( "" ); print_logger( "" ); print_logger( "" ); print_logger( " The following tests were executed: " ); print_logger( "" );
for( new test_index = 0; test_index < testsNumber; test_index++ ) { ArrayGetString( g_test_idsAndNamesArray, test_index, test_name, charsmax( test_name ) ); print_logger( " %3d. %s", test_index + 1, test_name ); } }
stock print_tests_failure() { LOGGER( 0, "I AM ENTERING ON print_tests_failure(0)" )
new test_id; new test_name[ MAX_SHORT_STRING ]; new failure_reason[ MAX_LONG_STRING ];
new failureTestsNumber = ArraySize( g_test_failureIdsArray );
if( failureTestsNumber ) { print_logger( "" ); print_logger( "" ); print_logger( " The following %s's Unit Tests failed: ", PLUGIN_NAME ); print_logger( "" );
for( new failure_index = 0; failure_index < failureTestsNumber; failure_index++ ) { test_id = ArrayGetCell( g_test_failureIdsArray, failure_index );
ArrayGetString( g_test_idsAndNamesArray, test_id - 1, test_name, charsmax( test_name ) ); ArrayGetString( g_test_failureReasonsArray, failure_index, failure_reason, charsmax( failure_reason ) );
print_logger( " %3d. %s: %s", test_id, test_name, failure_reason ); } }
print_logger( "" ); print_logger( " %d tests succeed.", g_test_testsNumber - g_test_failureNumber ); print_logger( " %d tests failed.", g_test_failureNumber ); }
/** * Informs the Test System that the test failed and why. * * @param test_id the test_id at the Test System * @param isFailure a boolean value setting whether the failure status is true. * @param failure_reason the reason why the test failed */ stock setTestFailure( test_id, bool:isFailure, failure_reason[] ) { LOGGER( 0, "I AM ENTERING ON setTestFailure(...) | test_id: %d, isFailure: %d, \ failure_reason: %s", test_id, isFailure, failure_reason )
new trieKey[ 10 ]; num_to_str( test_id, trieKey, charsmax( trieKey ) );
if( isFailure && !TrieKeyExists( g_test_failureIdsTrie, trieKey ) ) { g_test_failureNumber++;
ArrayPushCell( g_test_failureIdsArray, test_id ); TrieSetCell( g_test_failureIdsTrie, trieKey, 0 );
ArrayPushString( g_test_failureReasonsArray, failure_reason ); print_logger( " TEST FAILURE! %s", failure_reason );
return true; }
return false; }
/** * This is the first thing called when a test begin running. It function is to let the Test System * know that the test exists and then know how to handle it using the test_id. * * @param test_name the test name to register * * @return test_id an integer that refers it at the Test System. */ stock register_test( test_name[] ) { LOGGER( 0, "I AM ENTERING ON register_test(2) | test_name: %s", test_name )
ArrayPushString( g_test_idsAndNamesArray, test_name ); displaysLastTestOk();
g_test_testsNumber++; print_logger( " EXECUTING TEST %d AFTER %d SECONDS - %s ", g_test_testsNumber, computeTheTestElapsedTime(),test_name );
return g_test_testsNumber; }
/** * Register a test series naming, used to easily allow to distinguish between tests names. * Example, this: * 1. test_loadVoteChoices.aa_case1 * 2. test_loadVoteChoices.aa_case2 * 3. test_loadVoteChoices.bb.bb_case1 * 4. test_loadVoteChoices.bb.bb_case2 * * Instead of this: * 1. test_loadVoteChoices.a_case1 * 2. test_loadVoteChoices.a_case2 * 3. test_loadVoteChoices.b_case1 * 4. test_loadVoteChoices.b_case2 * * @param seriesName the current test name. * @param newSeries a char as the new test series start. The default is to use the last serie. */ stock test_registerSeriesNaming( seriesName[], newSeries = 0 ) { new currentIndex; new testName[ MAX_SHORT_STRING ];
static indentation; static currentSerie; static currentCaseNumber; static intentationLevel[ MAX_SHORT_STRING ];
currentCaseNumber++;
if( newSeries && newSeries != currentSerie ) { currentSerie = newSeries; indentation = ( ( currentSerie - 'a' + 1 ) * 3 ) % 15; currentCaseNumber = 1;
for( currentIndex = 0; currentIndex < indentation; ++currentIndex ) { if( currentIndex % 3 == 0 ) { intentationLevel[ currentIndex ] = '.'; } else { intentationLevel[ currentIndex ] = currentSerie; } }
intentationLevel[ currentIndex ] = '^0'; }
formatex( testName, charsmax( testName ), "%s%s_case%d", seriesName, intentationLevel, currentCaseNumber ); return register_test( testName ); }
stock configureTheUnitTests() { LOGGER( 128, "I AM ENTERING ON configureTheUnitTests(0)" )
print_logger( "" ); print_logger( "" ); print_logger( "" ); print_logger( "" ); print_logger( "" );
saveCurrentTestsTimeStamp()
g_test_failureIdsTrie = TrieCreate(); g_test_failureIdsArray = ArrayCreate( 1 ); g_test_failureReasonsArray = ArrayCreate( MAX_LONG_STRING ); g_test_idsAndNamesArray = ArrayCreate( MAX_SHORT_STRING ); }
stock printTheUnitTestsResults() { displaysLastTestOk(); print_logger( "" );
print_all_tests_executed(); print_tests_failure(); print_tests_results_count(); }
stock print_tests_results_count() { print_logger( "" ); print_logger( " Finished the %s's Unit Tests execution after '%d' seconds.", PLUGIN_NAME, computeTheTestElapsedTime() ); print_logger( "" ); print_logger( "" );
// Clear the time-stamp. g_test_lastTimeStamp = 0; }
/** * Compute how many days are elapsed since 1st January of 2000. * * @param currentDayInteger the current day from this year (1-366). * @param currentYearInteger the current year (2016). */ #define GET_CURRENT_BASED_DAY(%1,%2) ( ( %2 - 2000 ) * 366 + %1 )
/** * Save a time-stamp when the Unit Tests started to run. */ stock saveCurrentTestsTimeStamp() { if( !g_test_lastTimeStamp ) { new hour; new minute; new second;
time( hour, minute, second );
new currentDayInteger; new currentYearInteger; new rawTimeData[ 10 ];
get_time("%j", rawTimeData, charsmax( rawTimeData ) ); currentDayInteger = str_to_num( rawTimeData );
get_time("%Y", rawTimeData, charsmax( rawTimeData ) ); currentYearInteger = str_to_num( rawTimeData );
g_test_lastTimeStamp = hour * 3600 + minute * 60 + second; g_test_startDayInteger = GET_CURRENT_BASED_DAY( currentDayInteger, currentYearInteger ); } }
/** * Calculates how much time took to run the Unit Tests. For this to work, the stock * 'saveCurrentTestsTimeStamp(0)' must to be called on the beginning of the tests. * * @return seconds how much seconds are elapsed, or -1 on when the time-stamp is null. */ stock computeTheTestElapsedTime() { if( g_test_lastTimeStamp ) { new hour; new minute; new second; new delayResulted;
time( hour, minute, second );
new currentDayInteger; new currentYearInteger; new rawTimeData[ 10 ];
get_time("%j", rawTimeData, charsmax( rawTimeData ) ); currentDayInteger = str_to_num( rawTimeData );
get_time("%Y", rawTimeData, charsmax( rawTimeData ) ); currentYearInteger = str_to_num( rawTimeData );
delayResulted = hour * 3600 + minute * 60 + second - g_test_lastTimeStamp + 1; currentDayInteger = GET_CURRENT_BASED_DAY( currentDayInteger, currentYearInteger );
if( g_test_startDayInteger != currentDayInteger ) { // end - start = delay // 1:59 - 1:55 = 4 // 2:05 - 1:59 = -54 plus + 1 * 60 = 6 seconds (correct) // 3:05 - 1:59 = -54 plus + 2 * 60 = 66 seconds (correct) return delayResulted + ( currentDayInteger - g_test_startDayInteger ) * 86400; }
return delayResulted; }
return -1; }
public plugin_end() { TRY_TO_APPLY( ArrayDestroy, g_test_idsAndNamesArray ) TRY_TO_APPLY( ArrayDestroy, g_test_failureIdsArray ) TRY_TO_APPLY( ArrayDestroy, g_test_failureReasonsArray ) TRY_TO_APPLY( TrieDestroy, g_test_failureIdsTrie ) } // ################################# Unit Tests Body End ######################################
__________________
Last edited by addons_zz; 12-29-2016 at 21:13.
Reason: Updated the Unit Tests code
|
|