Fossil SCM
Add a test program for MSVC C89 implementations of rint() and snprintf()
Commit
c945679735b0c973499021e6ae772c2e8423484928d7c5f8a39e1bcf519fefd2
Parent
da9f362015a3f77…
1 file changed
+316
| --- a/test/test-msc89-rint-snprintf.c | ||
| +++ b/test/test-msc89-rint-snprintf.c | ||
| @@ -0,0 +1,316 @@ | ||
| 1 | +/* | |
| 2 | +** Test MSVC C89 compatible implementations of the following C99 functions: | |
| 3 | +** - double rint( double x ) | |
| 4 | +** Rounds a floating-point value to the nearest integer in floating-point | |
| 5 | +** format. | |
| 6 | +** - int snprintf( char *buffer, size_t count, const char *format, ... ) | |
| 7 | +** Writes formatted data to a string. | |
| 8 | +** | |
| 9 | +** NOTE: These implementations aim to provide the main functionality, not | |
| 10 | +** the exact behavior as specified in C99 standard. | |
| 11 | +** | |
| 12 | +** BUILD: cl test-msc98-rint-snprintf.c | |
| 13 | +** gcc test-msc98-rint-snprintf.c -lm ## for reference vs. non-MSVC | |
| 14 | +*/ | |
| 15 | + | |
| 16 | +#include <stdio.h> | |
| 17 | +#include <stdlib.h> | |
| 18 | +#include <string.h> | |
| 19 | +#include <math.h> | |
| 20 | +#include <limits.h> | |
| 21 | + | |
| 22 | +#define TEST_MSC89 1 | |
| 23 | + | |
| 24 | +#if defined(_MSC_VER) | |
| 25 | +#if (defined(TEST_MSC89) || (_MSC_VER < 1900)) /* before MSVC 2015 */ | |
| 26 | +#include <stdarg.h> | |
| 27 | + | |
| 28 | +/* NOTE: On truncation, this version of snprintf returns the input count, not | |
| 29 | +** the expected number of chars to fully output the requested format (as | |
| 30 | +** done in the C99 standard implementation). However the truncation test should | |
| 31 | +** still be applicable (nret >= count). | |
| 32 | +*/ | |
| 33 | +static __forceinline | |
| 34 | +int c89_snprintf(char *buf, size_t count, const char *fmt, ...){ | |
| 35 | + va_list argptr; | |
| 36 | + int n; | |
| 37 | + if( count==0 ) return 0; | |
| 38 | + va_start(argptr, fmt); | |
| 39 | + n = _vsprintf_p(buf, count, fmt, argptr); | |
| 40 | + va_end(argptr); | |
| 41 | + | |
| 42 | + /* force zero-termination to avoid some known MSVC bugs */ | |
| 43 | + if( count>0 ){ | |
| 44 | + buf[count - 1] = '\0'; | |
| 45 | + if( n<0 ) n = count; | |
| 46 | + } | |
| 47 | + return n; | |
| 48 | +} | |
| 49 | + | |
| 50 | +#if defined(_WIN64) | |
| 51 | +#include <emmintrin.h> | |
| 52 | +#include <limits.h> | |
| 53 | + | |
| 54 | +static __forceinline | |
| 55 | +double c89_rint(double v){ | |
| 56 | + return ( v<0.0 && v>=-0.5 ? -0.0 | |
| 57 | + : ( v!=0 && v>LLONG_MIN && v<LLONG_MAX | |
| 58 | + ? _mm_cvtsd_si64(_mm_load_sd(&v)) : v ) ); /* SSE2 */ | |
| 59 | +} | |
| 60 | +#else | |
| 61 | +static __forceinline | |
| 62 | +double c89_rint(double v){ | |
| 63 | + double rn; | |
| 64 | + __asm | |
| 65 | + { | |
| 66 | + FLD v | |
| 67 | + FRNDINT | |
| 68 | + FSTP rn | |
| 69 | + FWAIT | |
| 70 | + }; | |
| 71 | + return rn; | |
| 72 | +} | |
| 73 | +#endif /* _WIN64 */ | |
| 74 | +#endif /* (defined(TEST_MSC89) || (_MSC_VER < 1900)) */ | |
| 75 | + | |
| 76 | +#if (_MSC_VER < 1900) /* before MSVC 2015 */ | |
| 77 | +# define snprintf c89_snprintf | |
| 78 | +# define rint c89_rint | |
| 79 | +#else | |
| 80 | +# define HAVE_C99_RINT 1 | |
| 81 | +#endif | |
| 82 | + | |
| 83 | +#elif !defined(_MSC_VER) | |
| 84 | +# define HAVE_C99_RINT 1 | |
| 85 | +# define c89_snprintf snprintf | |
| 86 | +# define c89_rint rint | |
| 87 | +#endif /* defined(_MSC_VER) */ | |
| 88 | + | |
| 89 | + | |
| 90 | +#include <assert.h> | |
| 91 | + | |
| 92 | +#define SNPRINTF c89_snprintf | |
| 93 | +#define RINT c89_rint | |
| 94 | + | |
| 95 | +int test_rint() | |
| 96 | +{ | |
| 97 | + const char *TESTNAME = "rint"; | |
| 98 | + const struct test_data { | |
| 99 | + double v, expected; | |
| 100 | + } data[] = { /* round to the nearest or even integer */ | |
| 101 | +#ifdef HAVE_C99_RINT | |
| 102 | + {INFINITY,INFINITY}, | |
| 103 | +#endif | |
| 104 | + {(double)(LLONG_MAX/10000LL) + 0.7,(double)(LLONG_MAX/10000LL) + 1.}, | |
| 105 | + {5.5,6.},{5.4,5.},{5.2,5.},{5.,5.}, | |
| 106 | + {4.9,5.},{4.5,4.},{4.4,4.},{4.0,4.}, | |
| 107 | + {3.7,4.},{3.5,4.},{3.2,3.},{3.0,3.}, | |
| 108 | + {2.7,3.},{2.5,2.},{2.2,2.},{2.0,2.}, | |
| 109 | + {1.6,2.},{1.5,2.0},{1.3,1.0},{1.0,1.0}, | |
| 110 | + {0.9,1.},{0.8,1.},{0.5,0.},{0.49999999999999994,0.}, | |
| 111 | + {0.4,0.},{0.1,0.},{0.,0.} | |
| 112 | + }; | |
| 113 | + const size_t ndata = sizeof(data)/sizeof(data[0]); | |
| 114 | + int nfailed = 0; | |
| 115 | + int start = 0, end = ndata; | |
| 116 | + int i; | |
| 117 | + int done = 0; | |
| 118 | + | |
| 119 | + /* do two passes over the test data (positives, negatives) */ | |
| 120 | + do { | |
| 121 | + double sign = ( start<end ? 1. : -1. ); | |
| 122 | + for(i=start; ( start< end ? i<end : i>end ); ( start< end ? ++i : --i )){ | |
| 123 | + int passed = 0; | |
| 124 | + double v = sign*data[i].v; | |
| 125 | + double rn = c89_rint(v); | |
| 126 | + double expected = sign*data[i].expected; | |
| 127 | +#ifdef HAVE_C99_RINT | |
| 128 | + { | |
| 129 | + double rint_expected = rint(v); | |
| 130 | + int matched = ( expected==rint_expected ); | |
| 131 | + if( !matched ){ | |
| 132 | + fprintf(stderr, "E:%s|Expected test data[%d]={%.17lf,%.1lf} does not match the actual rint() value=%.1lf\n",__FUNCTION__, | |
| 133 | + i, v, expected, rint_expected); | |
| 134 | + } | |
| 135 | + assert(matched); | |
| 136 | + expected = rint_expected; | |
| 137 | + } | |
| 138 | +#endif | |
| 139 | + passed = ( rn==expected ); | |
| 140 | + fprintf(( passed ? stdout : stderr ), | |
| 141 | + "T:%s|c89_rint(%.17lf)=%.1lf expected=%.1lf\t[%s]\n", TESTNAME, | |
| 142 | + v, rn, expected, | |
| 143 | + ( passed ? "PASS" : "FAIL" )); | |
| 144 | + if( !passed ) ++nfailed; | |
| 145 | + } | |
| 146 | + | |
| 147 | + if( start<end ){ | |
| 148 | + int swap = start; | |
| 149 | + start = end - 1; end = swap - 1; | |
| 150 | + }else{ | |
| 151 | + done = 1; | |
| 152 | + } | |
| 153 | + }while( !done ); | |
| 154 | + | |
| 155 | + if( nfailed ){ | |
| 156 | + fprintf(stderr,"T:%s|FAILED %d test\n\n", TESTNAME, nfailed); | |
| 157 | + }else{ | |
| 158 | + printf("T:%s|PASSED\n\n", TESTNAME); | |
| 159 | + } | |
| 160 | + | |
| 161 | + return nfailed; | |
| 162 | +} | |
| 163 | + | |
| 164 | +int test_snprintf() | |
| 165 | +{ | |
| 166 | + const char *TESTNAME = "snprintf"; | |
| 167 | + int nfailed = 0; | |
| 168 | +#define TEST_BUF_MAXSIZE 256 | |
| 169 | + const struct test_data { | |
| 170 | + size_t bufsize; | |
| 171 | + const char *fmt, *expected, *full; | |
| 172 | + } data[] = { | |
| 173 | + {TEST_BUF_MAXSIZE,"snprintf(buf, %d)","snprintf(buf, 17)","snprintf(buf, 17)"}, | |
| 174 | + {17,"snprintf(buf, %d)","snprintf(buf, 17","snprintf(buf, 17)"}, | |
| 175 | + {2,"snprintf(buf, %d)","s","snprintf(buf, 17)"}, | |
| 176 | + {0,"snprintf(buf, %d)","","snprintf(buf, 17)"}, | |
| 177 | + }; | |
| 178 | + const size_t ndata = sizeof(data)/sizeof(data[0]); | |
| 179 | + char buf[TEST_BUF_MAXSIZE] = {0}; | |
| 180 | + int i; | |
| 181 | + | |
| 182 | + for(i=0; i<ndata; ++i){ | |
| 183 | + int passed = 0; | |
| 184 | + size_t count = data[i].bufsize; | |
| 185 | + const char *fmt = data[i].fmt; | |
| 186 | + const char *full = data[i].full; | |
| 187 | + const char *expected = data[i].expected; | |
| 188 | + const int truncate_expected = ( count<=strlen(full) ); | |
| 189 | + const int expected_nret = ( !truncate_expected | |
| 190 | + ? strlen(expected) : count ); | |
| 191 | + const int expected_zero_at = ( !truncate_expected | |
| 192 | + ? expected_nret : expected_nret - 1 ); | |
| 193 | + int nret; | |
| 194 | + buf[( count>0 ? count - 1 : 0 )] = '\0'; | |
| 195 | + nret = c89_snprintf(buf, count, fmt, strlen(fmt)); | |
| 196 | +#ifdef HAVE_C99_RINT | |
| 197 | + { | |
| 198 | + char snprintf_expected[TEST_BUF_MAXSIZE] = {0}; | |
| 199 | + int snprintf_expected_nret = snprintf(snprintf_expected, count, fmt, strlen(fmt)); | |
| 200 | + int matched = ( strcmp(expected, snprintf_expected)==0 ); | |
| 201 | + int matched_nret = ( expected_nret==snprintf_expected_nret ); | |
| 202 | + if( !matched ){ | |
| 203 | + fprintf(stderr, "E:%s|Expected test data[%d]={'%s','%s'} does not match the actual snprintf() value='%s'\n",__FUNCTION__, | |
| 204 | + i, buf, expected, snprintf_expected); | |
| 205 | + } | |
| 206 | + | |
| 207 | + /* NOTE: This implementation of c89_snprintf() on truncation returns | |
| 208 | + ** the input count, not the expected number of chars needed to fully | |
| 209 | + ** output the requested format. So warn, only ifthe expected nret is | |
| 210 | + ** less than the count. | |
| 211 | + */ | |
| 212 | + if( !matched_nret && expected_nret<count ){ | |
| 213 | + fprintf(stderr, "W:%s|Expected return value=%d for test data[%d]={'%s','%s'} does not match the actual snprintf() return value=%d\n",__FUNCTION__, | |
| 214 | + expected_nret, i, buf, expected, snprintf_expected_nret); | |
| 215 | + } | |
| 216 | + assert(matched); | |
| 217 | + expected = snprintf_expected; | |
| 218 | + } | |
| 219 | +#endif | |
| 220 | + passed = ( nret==expected_nret ); | |
| 221 | + fprintf(( passed ? stdout : stderr ), | |
| 222 | + "T:%s|c89_snprintf(%lu,\"%s\", %d) nret=%d expected=%d\t[%s]\n", TESTNAME, | |
| 223 | + (unsigned long)count, fmt, (int)strlen(fmt), nret, expected_nret, | |
| 224 | + ( passed ? "PASS" : "FAIL" )); | |
| 225 | + if( !passed ) ++nfailed; | |
| 226 | + | |
| 227 | + if( count ){ | |
| 228 | + passed = ( buf[expected_zero_at]=='\0' ); | |
| 229 | + fprintf(( passed ? stdout : stderr ), | |
| 230 | + "T:%s|c89_snprintf(%lu,\"%s\", %d) s[%d]=%d expected=%d\t[%s]\n", TESTNAME, | |
| 231 | + (unsigned long)count, fmt, (int)strlen(fmt), expected_zero_at, buf[expected_zero_at],'\0', | |
| 232 | + ( passed ? "PASS" : "FAIL" )); | |
| 233 | + if( !passed ) ++nfailed; | |
| 234 | + } | |
| 235 | + | |
| 236 | + passed = ( strcmp(buf, expected)==0 ); | |
| 237 | + fprintf((passed ? stdout : stderr), | |
| 238 | + "T:%s|c89_snprintf(%lu,\"%s\", %d)=\"%s\" expected=\"%s\"\t[%s]\n", TESTNAME, | |
| 239 | + (unsigned long)count, fmt, (int)strlen(fmt), buf, expected, | |
| 240 | + ( passed ? "PASS" : "FAIL" )); | |
| 241 | + if( !passed ) ++nfailed; | |
| 242 | + } | |
| 243 | + | |
| 244 | + if( nfailed ){ | |
| 245 | + fprintf(stderr,"T:%s|FAILED %d tests\n\n", TESTNAME, nfailed); | |
| 246 | + }else{ | |
| 247 | + printf("T:%s|PASSED\n\n", TESTNAME); | |
| 248 | + } | |
| 249 | + return nfailed; | |
| 250 | +} | |
| 251 | + | |
| 252 | +int main(int argc, char *argv[]) | |
| 253 | +{ | |
| 254 | + int iarg; | |
| 255 | + struct testrun { | |
| 256 | + char rint, snprintf; | |
| 257 | + int status; | |
| 258 | + } testrun = {0, 0, 0}; | |
| 259 | + | |
| 260 | + printf("Usage: %s [TEST]...\n", argv[0]); | |
| 261 | + printf("Test MSVC C89 compatible implementations of selected C99 functions.\n" | |
| 262 | + "Run the selected TEST, by default 'all'; optionally exclude tests from the run.\n"); | |
| 263 | + printf("Example: %s all -snprintf\n", argv[0]); | |
| 264 | + printf("\nTests:\n" | |
| 265 | + " all, rint, snprintf\n" | |
| 266 | + "\n -TEST\t\texclude the test from the run\n" | |
| 267 | + "\n" | |
| 268 | + ); | |
| 269 | + if( argc>1 | |
| 270 | + && ( strcmp(argv[1], "/?")==0 || strcmp(argv[1], "/h")==0 | |
| 271 | + || strcmp(argv[1], "/H")==0 || strcmp(argv[1], "--help")==0 ) ){ | |
| 272 | + return 0; | |
| 273 | + } | |
| 274 | + | |
| 275 | + testrun.rint = testrun.snprintf = ( argc==1 ); | |
| 276 | + for(iarg=1; iarg<argc; ++iarg){ | |
| 277 | + const char *name = argv[iarg]; | |
| 278 | + char runit = 1; | |
| 279 | + if( argv[iarg][0]=='-' ){ | |
| 280 | + runit = 0; | |
| 281 | + name = &(argv[iarg][1]); | |
| 282 | + } | |
| 283 | + | |
| 284 | + if( strcmp(name, "all")==0 ){ | |
| 285 | + testrun.rint = testrun.snprintf = runit; | |
| 286 | + }else if( strcmp(name, "rint")==0 ){ | |
| 287 | + testrun.rint = runit; | |
| 288 | + }else if( strcmp(name, "snprintf")==0 ){ | |
| 289 | + testrun.snprintf = runit; | |
| 290 | + }else{ | |
| 291 | + testrun.status = 2; | |
| 292 | + fprintf(stderr, "\nE|Invalid test requested: '%s'\n", name); | |
| 293 | + } | |
| 294 | + } | |
| 295 | + | |
| 296 | + if( testrun.status==2 ){ | |
| 297 | + return testrun.status; | |
| 298 | + } | |
| 299 | + | |
| 300 | +#ifndef _MSC_VER | |
| 301 | + fprintf(stderr, "W|Non-MSVC mode: testing against the native implementations\n\n"); | |
| 302 | +#endif | |
| 303 | + | |
| 304 | + if( testrun.rint ) | |
| 305 | + testrun.status |= test_rint(); | |
| 306 | + | |
| 307 | + if( testrun.snprintf ) | |
| 308 | + testrun.status |= test_snprintf(); | |
| 309 | + | |
| 310 | + if( testrun.status==0 ) | |
| 311 | + printf("\nI|All selected tests completed successfully\n"); | |
| 312 | + else | |
| 313 | + fprintf(stderr, "\nE|Some of the selected tests failed\n"); | |
| 314 | + return testrun.status; | |
| 315 | +} | |
| 316 | + |
| --- a/test/test-msc89-rint-snprintf.c | |
| +++ b/test/test-msc89-rint-snprintf.c | |
| @@ -0,0 +1,316 @@ | |
| --- a/test/test-msc89-rint-snprintf.c | |
| +++ b/test/test-msc89-rint-snprintf.c | |
| @@ -0,0 +1,316 @@ | |
| 1 | /* |
| 2 | ** Test MSVC C89 compatible implementations of the following C99 functions: |
| 3 | ** - double rint( double x ) |
| 4 | ** Rounds a floating-point value to the nearest integer in floating-point |
| 5 | ** format. |
| 6 | ** - int snprintf( char *buffer, size_t count, const char *format, ... ) |
| 7 | ** Writes formatted data to a string. |
| 8 | ** |
| 9 | ** NOTE: These implementations aim to provide the main functionality, not |
| 10 | ** the exact behavior as specified in C99 standard. |
| 11 | ** |
| 12 | ** BUILD: cl test-msc98-rint-snprintf.c |
| 13 | ** gcc test-msc98-rint-snprintf.c -lm ## for reference vs. non-MSVC |
| 14 | */ |
| 15 | |
| 16 | #include <stdio.h> |
| 17 | #include <stdlib.h> |
| 18 | #include <string.h> |
| 19 | #include <math.h> |
| 20 | #include <limits.h> |
| 21 | |
| 22 | #define TEST_MSC89 1 |
| 23 | |
| 24 | #if defined(_MSC_VER) |
| 25 | #if (defined(TEST_MSC89) || (_MSC_VER < 1900)) /* before MSVC 2015 */ |
| 26 | #include <stdarg.h> |
| 27 | |
| 28 | /* NOTE: On truncation, this version of snprintf returns the input count, not |
| 29 | ** the expected number of chars to fully output the requested format (as |
| 30 | ** done in the C99 standard implementation). However the truncation test should |
| 31 | ** still be applicable (nret >= count). |
| 32 | */ |
| 33 | static __forceinline |
| 34 | int c89_snprintf(char *buf, size_t count, const char *fmt, ...){ |
| 35 | va_list argptr; |
| 36 | int n; |
| 37 | if( count==0 ) return 0; |
| 38 | va_start(argptr, fmt); |
| 39 | n = _vsprintf_p(buf, count, fmt, argptr); |
| 40 | va_end(argptr); |
| 41 | |
| 42 | /* force zero-termination to avoid some known MSVC bugs */ |
| 43 | if( count>0 ){ |
| 44 | buf[count - 1] = '\0'; |
| 45 | if( n<0 ) n = count; |
| 46 | } |
| 47 | return n; |
| 48 | } |
| 49 | |
| 50 | #if defined(_WIN64) |
| 51 | #include <emmintrin.h> |
| 52 | #include <limits.h> |
| 53 | |
| 54 | static __forceinline |
| 55 | double c89_rint(double v){ |
| 56 | return ( v<0.0 && v>=-0.5 ? -0.0 |
| 57 | : ( v!=0 && v>LLONG_MIN && v<LLONG_MAX |
| 58 | ? _mm_cvtsd_si64(_mm_load_sd(&v)) : v ) ); /* SSE2 */ |
| 59 | } |
| 60 | #else |
| 61 | static __forceinline |
| 62 | double c89_rint(double v){ |
| 63 | double rn; |
| 64 | __asm |
| 65 | { |
| 66 | FLD v |
| 67 | FRNDINT |
| 68 | FSTP rn |
| 69 | FWAIT |
| 70 | }; |
| 71 | return rn; |
| 72 | } |
| 73 | #endif /* _WIN64 */ |
| 74 | #endif /* (defined(TEST_MSC89) || (_MSC_VER < 1900)) */ |
| 75 | |
| 76 | #if (_MSC_VER < 1900) /* before MSVC 2015 */ |
| 77 | # define snprintf c89_snprintf |
| 78 | # define rint c89_rint |
| 79 | #else |
| 80 | # define HAVE_C99_RINT 1 |
| 81 | #endif |
| 82 | |
| 83 | #elif !defined(_MSC_VER) |
| 84 | # define HAVE_C99_RINT 1 |
| 85 | # define c89_snprintf snprintf |
| 86 | # define c89_rint rint |
| 87 | #endif /* defined(_MSC_VER) */ |
| 88 | |
| 89 | |
| 90 | #include <assert.h> |
| 91 | |
| 92 | #define SNPRINTF c89_snprintf |
| 93 | #define RINT c89_rint |
| 94 | |
| 95 | int test_rint() |
| 96 | { |
| 97 | const char *TESTNAME = "rint"; |
| 98 | const struct test_data { |
| 99 | double v, expected; |
| 100 | } data[] = { /* round to the nearest or even integer */ |
| 101 | #ifdef HAVE_C99_RINT |
| 102 | {INFINITY,INFINITY}, |
| 103 | #endif |
| 104 | {(double)(LLONG_MAX/10000LL) + 0.7,(double)(LLONG_MAX/10000LL) + 1.}, |
| 105 | {5.5,6.},{5.4,5.},{5.2,5.},{5.,5.}, |
| 106 | {4.9,5.},{4.5,4.},{4.4,4.},{4.0,4.}, |
| 107 | {3.7,4.},{3.5,4.},{3.2,3.},{3.0,3.}, |
| 108 | {2.7,3.},{2.5,2.},{2.2,2.},{2.0,2.}, |
| 109 | {1.6,2.},{1.5,2.0},{1.3,1.0},{1.0,1.0}, |
| 110 | {0.9,1.},{0.8,1.},{0.5,0.},{0.49999999999999994,0.}, |
| 111 | {0.4,0.},{0.1,0.},{0.,0.} |
| 112 | }; |
| 113 | const size_t ndata = sizeof(data)/sizeof(data[0]); |
| 114 | int nfailed = 0; |
| 115 | int start = 0, end = ndata; |
| 116 | int i; |
| 117 | int done = 0; |
| 118 | |
| 119 | /* do two passes over the test data (positives, negatives) */ |
| 120 | do { |
| 121 | double sign = ( start<end ? 1. : -1. ); |
| 122 | for(i=start; ( start< end ? i<end : i>end ); ( start< end ? ++i : --i )){ |
| 123 | int passed = 0; |
| 124 | double v = sign*data[i].v; |
| 125 | double rn = c89_rint(v); |
| 126 | double expected = sign*data[i].expected; |
| 127 | #ifdef HAVE_C99_RINT |
| 128 | { |
| 129 | double rint_expected = rint(v); |
| 130 | int matched = ( expected==rint_expected ); |
| 131 | if( !matched ){ |
| 132 | fprintf(stderr, "E:%s|Expected test data[%d]={%.17lf,%.1lf} does not match the actual rint() value=%.1lf\n",__FUNCTION__, |
| 133 | i, v, expected, rint_expected); |
| 134 | } |
| 135 | assert(matched); |
| 136 | expected = rint_expected; |
| 137 | } |
| 138 | #endif |
| 139 | passed = ( rn==expected ); |
| 140 | fprintf(( passed ? stdout : stderr ), |
| 141 | "T:%s|c89_rint(%.17lf)=%.1lf expected=%.1lf\t[%s]\n", TESTNAME, |
| 142 | v, rn, expected, |
| 143 | ( passed ? "PASS" : "FAIL" )); |
| 144 | if( !passed ) ++nfailed; |
| 145 | } |
| 146 | |
| 147 | if( start<end ){ |
| 148 | int swap = start; |
| 149 | start = end - 1; end = swap - 1; |
| 150 | }else{ |
| 151 | done = 1; |
| 152 | } |
| 153 | }while( !done ); |
| 154 | |
| 155 | if( nfailed ){ |
| 156 | fprintf(stderr,"T:%s|FAILED %d test\n\n", TESTNAME, nfailed); |
| 157 | }else{ |
| 158 | printf("T:%s|PASSED\n\n", TESTNAME); |
| 159 | } |
| 160 | |
| 161 | return nfailed; |
| 162 | } |
| 163 | |
| 164 | int test_snprintf() |
| 165 | { |
| 166 | const char *TESTNAME = "snprintf"; |
| 167 | int nfailed = 0; |
| 168 | #define TEST_BUF_MAXSIZE 256 |
| 169 | const struct test_data { |
| 170 | size_t bufsize; |
| 171 | const char *fmt, *expected, *full; |
| 172 | } data[] = { |
| 173 | {TEST_BUF_MAXSIZE,"snprintf(buf, %d)","snprintf(buf, 17)","snprintf(buf, 17)"}, |
| 174 | {17,"snprintf(buf, %d)","snprintf(buf, 17","snprintf(buf, 17)"}, |
| 175 | {2,"snprintf(buf, %d)","s","snprintf(buf, 17)"}, |
| 176 | {0,"snprintf(buf, %d)","","snprintf(buf, 17)"}, |
| 177 | }; |
| 178 | const size_t ndata = sizeof(data)/sizeof(data[0]); |
| 179 | char buf[TEST_BUF_MAXSIZE] = {0}; |
| 180 | int i; |
| 181 | |
| 182 | for(i=0; i<ndata; ++i){ |
| 183 | int passed = 0; |
| 184 | size_t count = data[i].bufsize; |
| 185 | const char *fmt = data[i].fmt; |
| 186 | const char *full = data[i].full; |
| 187 | const char *expected = data[i].expected; |
| 188 | const int truncate_expected = ( count<=strlen(full) ); |
| 189 | const int expected_nret = ( !truncate_expected |
| 190 | ? strlen(expected) : count ); |
| 191 | const int expected_zero_at = ( !truncate_expected |
| 192 | ? expected_nret : expected_nret - 1 ); |
| 193 | int nret; |
| 194 | buf[( count>0 ? count - 1 : 0 )] = '\0'; |
| 195 | nret = c89_snprintf(buf, count, fmt, strlen(fmt)); |
| 196 | #ifdef HAVE_C99_RINT |
| 197 | { |
| 198 | char snprintf_expected[TEST_BUF_MAXSIZE] = {0}; |
| 199 | int snprintf_expected_nret = snprintf(snprintf_expected, count, fmt, strlen(fmt)); |
| 200 | int matched = ( strcmp(expected, snprintf_expected)==0 ); |
| 201 | int matched_nret = ( expected_nret==snprintf_expected_nret ); |
| 202 | if( !matched ){ |
| 203 | fprintf(stderr, "E:%s|Expected test data[%d]={'%s','%s'} does not match the actual snprintf() value='%s'\n",__FUNCTION__, |
| 204 | i, buf, expected, snprintf_expected); |
| 205 | } |
| 206 | |
| 207 | /* NOTE: This implementation of c89_snprintf() on truncation returns |
| 208 | ** the input count, not the expected number of chars needed to fully |
| 209 | ** output the requested format. So warn, only ifthe expected nret is |
| 210 | ** less than the count. |
| 211 | */ |
| 212 | if( !matched_nret && expected_nret<count ){ |
| 213 | fprintf(stderr, "W:%s|Expected return value=%d for test data[%d]={'%s','%s'} does not match the actual snprintf() return value=%d\n",__FUNCTION__, |
| 214 | expected_nret, i, buf, expected, snprintf_expected_nret); |
| 215 | } |
| 216 | assert(matched); |
| 217 | expected = snprintf_expected; |
| 218 | } |
| 219 | #endif |
| 220 | passed = ( nret==expected_nret ); |
| 221 | fprintf(( passed ? stdout : stderr ), |
| 222 | "T:%s|c89_snprintf(%lu,\"%s\", %d) nret=%d expected=%d\t[%s]\n", TESTNAME, |
| 223 | (unsigned long)count, fmt, (int)strlen(fmt), nret, expected_nret, |
| 224 | ( passed ? "PASS" : "FAIL" )); |
| 225 | if( !passed ) ++nfailed; |
| 226 | |
| 227 | if( count ){ |
| 228 | passed = ( buf[expected_zero_at]=='\0' ); |
| 229 | fprintf(( passed ? stdout : stderr ), |
| 230 | "T:%s|c89_snprintf(%lu,\"%s\", %d) s[%d]=%d expected=%d\t[%s]\n", TESTNAME, |
| 231 | (unsigned long)count, fmt, (int)strlen(fmt), expected_zero_at, buf[expected_zero_at],'\0', |
| 232 | ( passed ? "PASS" : "FAIL" )); |
| 233 | if( !passed ) ++nfailed; |
| 234 | } |
| 235 | |
| 236 | passed = ( strcmp(buf, expected)==0 ); |
| 237 | fprintf((passed ? stdout : stderr), |
| 238 | "T:%s|c89_snprintf(%lu,\"%s\", %d)=\"%s\" expected=\"%s\"\t[%s]\n", TESTNAME, |
| 239 | (unsigned long)count, fmt, (int)strlen(fmt), buf, expected, |
| 240 | ( passed ? "PASS" : "FAIL" )); |
| 241 | if( !passed ) ++nfailed; |
| 242 | } |
| 243 | |
| 244 | if( nfailed ){ |
| 245 | fprintf(stderr,"T:%s|FAILED %d tests\n\n", TESTNAME, nfailed); |
| 246 | }else{ |
| 247 | printf("T:%s|PASSED\n\n", TESTNAME); |
| 248 | } |
| 249 | return nfailed; |
| 250 | } |
| 251 | |
| 252 | int main(int argc, char *argv[]) |
| 253 | { |
| 254 | int iarg; |
| 255 | struct testrun { |
| 256 | char rint, snprintf; |
| 257 | int status; |
| 258 | } testrun = {0, 0, 0}; |
| 259 | |
| 260 | printf("Usage: %s [TEST]...\n", argv[0]); |
| 261 | printf("Test MSVC C89 compatible implementations of selected C99 functions.\n" |
| 262 | "Run the selected TEST, by default 'all'; optionally exclude tests from the run.\n"); |
| 263 | printf("Example: %s all -snprintf\n", argv[0]); |
| 264 | printf("\nTests:\n" |
| 265 | " all, rint, snprintf\n" |
| 266 | "\n -TEST\t\texclude the test from the run\n" |
| 267 | "\n" |
| 268 | ); |
| 269 | if( argc>1 |
| 270 | && ( strcmp(argv[1], "/?")==0 || strcmp(argv[1], "/h")==0 |
| 271 | || strcmp(argv[1], "/H")==0 || strcmp(argv[1], "--help")==0 ) ){ |
| 272 | return 0; |
| 273 | } |
| 274 | |
| 275 | testrun.rint = testrun.snprintf = ( argc==1 ); |
| 276 | for(iarg=1; iarg<argc; ++iarg){ |
| 277 | const char *name = argv[iarg]; |
| 278 | char runit = 1; |
| 279 | if( argv[iarg][0]=='-' ){ |
| 280 | runit = 0; |
| 281 | name = &(argv[iarg][1]); |
| 282 | } |
| 283 | |
| 284 | if( strcmp(name, "all")==0 ){ |
| 285 | testrun.rint = testrun.snprintf = runit; |
| 286 | }else if( strcmp(name, "rint")==0 ){ |
| 287 | testrun.rint = runit; |
| 288 | }else if( strcmp(name, "snprintf")==0 ){ |
| 289 | testrun.snprintf = runit; |
| 290 | }else{ |
| 291 | testrun.status = 2; |
| 292 | fprintf(stderr, "\nE|Invalid test requested: '%s'\n", name); |
| 293 | } |
| 294 | } |
| 295 | |
| 296 | if( testrun.status==2 ){ |
| 297 | return testrun.status; |
| 298 | } |
| 299 | |
| 300 | #ifndef _MSC_VER |
| 301 | fprintf(stderr, "W|Non-MSVC mode: testing against the native implementations\n\n"); |
| 302 | #endif |
| 303 | |
| 304 | if( testrun.rint ) |
| 305 | testrun.status |= test_rint(); |
| 306 | |
| 307 | if( testrun.snprintf ) |
| 308 | testrun.status |= test_snprintf(); |
| 309 | |
| 310 | if( testrun.status==0 ) |
| 311 | printf("\nI|All selected tests completed successfully\n"); |
| 312 | else |
| 313 | fprintf(stderr, "\nE|Some of the selected tests failed\n"); |
| 314 | return testrun.status; |
| 315 | } |
| 316 |