General Numeric Base Conversion - Any base to any base

For scripting examples for learning how to use AMXX.
User avatar
Archive-Poster
Posts: 1267
Gender:
United States of America

General Numeric Base Conversion - Any base to any base

Post by Archive-Poster »

General Numeric Base Conversion - Any base to any base

The stock:

Code: Select all

/**
 * 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:

Code: Select all

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:

Code: Select all

/** 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 ###################################### 
Attachments
full_unit_tests.sma.zip
(5.71 KiB) Downloaded 2 times

Who is online

Users browsing this forum: No registered users and 1 guest