mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-26 15:21:51 -08:00
Clang 10 turns on -Wimplicit-int-float-conversion, and on 64-bit platforms this issues a warning that implicit conversions to double from Size and unsigned long "may lose precision". This commit adds casts to (double) for all such conversions. The loss of precision is either impossible in practice (because a double can represent all integers up to 2**53, which is about 9 petabytes, well beyond the addressing capabilities for current CPUs), or else acceptable, because we are accumulating an approximate quantity like "collection work" or "fill size" (that has to cope with loss of precision due in any case), or computing a threshold like the spare commit limit where it is acceptable for it to be somewhat approximate.
470 lines
13 KiB
C
470 lines
13 KiB
C
/* testlib.c: TEST LIBRARY
|
|
*
|
|
* $Id$
|
|
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
|
* Portions copyright (C) 2002 Global Graphics Software.
|
|
*
|
|
* .purpose: A library of functions that may be of use to unit tests.
|
|
*/
|
|
|
|
#include "testlib.h"
|
|
#include "clock.h" /* for EVENT_CLOCK */
|
|
#include "mps.h"
|
|
#include "misc.h" /* for NOOP */
|
|
|
|
#include <math.h> /* fmod, log, HUGE_VAL */
|
|
#include <stdio.h> /* fflush, printf, stderr, sscanf, vfprintf */
|
|
#include <stdlib.h> /* abort, exit, getenv */
|
|
#include <time.h> /* time */
|
|
|
|
|
|
/* fail -- like assert, but (notionally) returns a value, so usable in an expression */
|
|
|
|
int fail(void)
|
|
{
|
|
Insist(FALSE);
|
|
return 1111UL;
|
|
}
|
|
|
|
|
|
/* rnd -- a random number generator
|
|
*
|
|
* We use the (Multiplicative) Linear Congruential Generator
|
|
* Xn = a * Xn-1 mod m
|
|
* with: m = 2147483647 (2^31 - 1, a Mersenne prime), and a = 48271.
|
|
* This is a 'full-period' generator: all values in [1..(mod-1)]
|
|
* (ie. 0x00000001 to 0x7ffffffe inclusive) are returned once, and then
|
|
* the cycle begins again. The value 0 is not part of the cycle and
|
|
* is never returned. So the period = mod-1, ie. 2147483646.
|
|
*
|
|
* This generator is extremely simple and has been very well studied.
|
|
* It is free of major vices we might care about for this application.
|
|
* In particular, as m is prime, low order bits are random. Therefore
|
|
* to roll an N-sided die (N << m), "rnd() % N" is acceptable, giving
|
|
* a value in [0..N-1].
|
|
*
|
|
* It was popularised by the much-cited Park & Miller paper:
|
|
* Stephen K Park & Keith W Miller (1988). Random number generators:
|
|
* good ones are hard to find. Communications of the ACM,
|
|
* 31:1192-1201.
|
|
* The recommended multiplier a was later updated from 16807 to 48271:
|
|
* Stephen K Park, Keith W Miller, Paul K. Stockmeyer (1993).
|
|
* Technical Correspondence. Communications of the ACM, 36:105-110.
|
|
*
|
|
* (Many more elaborate generators have been invented. The next simple
|
|
* step would be to combine with the MLCG m = 2147483399 a = 40692, to
|
|
* make the period "about 74 quadrillion". See the summary of chapter
|
|
* 3 in Knuth's "The Art of Computer Programming".)
|
|
*
|
|
* This (fast) implementation uses the identity:
|
|
* 0x80000000 == 0x7FFFFFFF + 0x00000001
|
|
* noted by David Carta (1990), where 0x7FFFFFFF == 2^31-1 == m, which
|
|
* means that bits above the first 31 can simply be shifted >> 31 and
|
|
* added, preserving Xn mod m. To remain within 32-bit unsigned
|
|
* arithmetic when multiplying the previous seed (31 bits) by a (16
|
|
* bits), the seed is split into bottom and top halves; bits above
|
|
* the first 31 are simply "top >> 16". (Code by RHSK, inspired by
|
|
* Robin Whittle's article at "http://www.firstpr.com.au/dsp/rand31/").
|
|
*
|
|
* Slower implementations, used for verification:
|
|
* rnd_verify_schrage uses the method of L. Schrage (1979 & 1983),
|
|
* namely splitting the seed by q, where q = m div a.
|
|
* rnd_verify_float simply uses floating point arithmetic.
|
|
*/
|
|
|
|
static unsigned long seed = 1;
|
|
#define R_m 2147483647UL
|
|
#define R_a 48271UL
|
|
unsigned long rnd(void)
|
|
{
|
|
/* requires m == 2^31-1, a < 2^16 */
|
|
unsigned long bot = R_a * (seed & 0x7FFF);
|
|
unsigned long top = R_a * (seed >> 15);
|
|
seed = bot + ((top & 0xFFFF) << 15) + (top >> 16);
|
|
if(seed > R_m)
|
|
seed -= R_m;
|
|
return seed;
|
|
/* Have you modified this code? Run rnd_verify(3) please! RHSK */
|
|
}
|
|
|
|
static unsigned long seed_verify_schrage = 1;
|
|
#define R_q (R_m / R_a)
|
|
#define R_r (R_m % R_a)
|
|
static unsigned long rnd_verify_schrage(void)
|
|
{
|
|
/* requires m < 2^31, q > r; see Park & Miller (1988) */
|
|
unsigned long alpha = R_a * (seed_verify_schrage % R_q); /* < m */
|
|
unsigned long beta = R_r * (seed_verify_schrage / R_q); /* < m */
|
|
seed_verify_schrage = alpha - beta;
|
|
if(alpha < beta)
|
|
seed_verify_schrage += R_m;
|
|
return seed_verify_schrage;
|
|
}
|
|
|
|
static unsigned long seed_verify_float = 1;
|
|
#define R_m_float 2147483647.0
|
|
#define R_a_float 48271.0
|
|
static unsigned long rnd_verify_float(void)
|
|
{
|
|
double s;
|
|
s = (double)seed_verify_float;
|
|
s *= R_a_float;
|
|
s = fmod(s, R_m_float);
|
|
seed_verify_float = (unsigned long)s;
|
|
return seed_verify_float;
|
|
}
|
|
|
|
/* rnd_verify -- verify that rnd() returns the correct results
|
|
*
|
|
* depth = how much time to spend verifying
|
|
* 0: very quick -- just verify the next rnd() value
|
|
* 1: quick -- verify the first 10000 calls from seed = 1
|
|
* 2: slow (~ 1 minute) -- run the fast generator for a full cycle
|
|
* 3: very slow (several minutes) -- verify a full cycle
|
|
*/
|
|
void rnd_verify(int depth)
|
|
{
|
|
unsigned long orig_seed = seed;
|
|
unsigned long i;
|
|
unsigned long r = 0;
|
|
|
|
/* 0: the next value from rnd() matches rnd_verify_*() */
|
|
if(depth >= 0) {
|
|
seed_verify_schrage = seed;
|
|
seed_verify_float = seed;
|
|
r = rnd();
|
|
Insist(rnd_verify_schrage() == r);
|
|
Insist(rnd_verify_float() == r);
|
|
}
|
|
|
|
/* 1: first 10000 (from Park & Miller, note: 1-based indexing!) */
|
|
if(depth >= 1) {
|
|
i = 1;
|
|
seed = 1;
|
|
seed_verify_schrage = seed;
|
|
seed_verify_float = seed;
|
|
for(i = 2; i <= 10001; i += 1) {
|
|
r = rnd();
|
|
Insist(rnd_verify_schrage() == r);
|
|
Insist(rnd_verify_float() == r);
|
|
}
|
|
/* Insist(r == 1043618065UL); -- correct value for a = 16807 */
|
|
Insist(r == 399268537UL); /* correct for a = 48271 */
|
|
}
|
|
|
|
/* 1: observe wrap-around (note: 0-based indexing) */
|
|
if(depth >= 1) {
|
|
/* set-up seed value for i = 2147483645 */
|
|
/* seed = 1407677000UL; -- correct value for a = 16807 */
|
|
seed = 1899818559UL; /* correct for a = 48271 */
|
|
seed_verify_schrage = seed;
|
|
seed_verify_float = seed;
|
|
r = rnd();
|
|
Insist(rnd_verify_schrage() == r);
|
|
Insist(rnd_verify_float() == r);
|
|
Insist(r == 1); /* wrap-around */
|
|
}
|
|
|
|
/* 2 & 3: Full cycle (3 => verifying each value) */
|
|
if(depth >= 2) {
|
|
int verify = (depth >= 3);
|
|
unsigned long r1 = 1;
|
|
|
|
i = 0;
|
|
seed = 1;
|
|
seed_verify_schrage = seed;
|
|
seed_verify_float = seed;
|
|
while(1) {
|
|
i += 1;
|
|
r = rnd();
|
|
if(verify) {
|
|
Insist(rnd_verify_schrage() == r);
|
|
Insist(rnd_verify_float() == r);
|
|
}
|
|
if(r == 1) {
|
|
printf("Full cycle complete%s:\n",
|
|
verify ? " (verifying every value)"
|
|
: " (fast implementation only)" );
|
|
printf("Wrapped at i=%lu, r=%lu, r(i-1)=%lu.\n",
|
|
i, r, r1);
|
|
break;
|
|
} else {
|
|
r1 = r;
|
|
}
|
|
}
|
|
}
|
|
|
|
seed = orig_seed;
|
|
}
|
|
|
|
/* rnd_addr -- a random address generator
|
|
*
|
|
* rnd gives 31 random bits, we run it repeatedly to get enough bits.
|
|
*/
|
|
|
|
#define ADDR_BITS (sizeof(mps_addr_t) * CHAR_BIT)
|
|
|
|
mps_addr_t rnd_addr(void)
|
|
{
|
|
mps_word_t res;
|
|
unsigned bits;
|
|
|
|
for (bits = 0, res = 0; bits < ADDR_BITS;
|
|
bits += 31, res = res << 31 | (mps_word_t)rnd())
|
|
NOOP;
|
|
return (mps_addr_t)res;
|
|
}
|
|
|
|
double rnd_double(void)
|
|
{
|
|
return (double)rnd() / R_m_float;
|
|
}
|
|
|
|
static unsigned sizelog2(size_t size)
|
|
{
|
|
return (unsigned)(log((double)size) / log(2.0));
|
|
}
|
|
|
|
size_t rnd_grain(size_t arena_size)
|
|
{
|
|
/* The grain size must be small enough to allow for a complete set
|
|
of zones in the initial chunk, but bigger than one word. */
|
|
Insist(arena_size >> MPS_WORD_SHIFT >= sizeof(void *));
|
|
return rnd_align(sizeof(void *), (size_t)1 << sizelog2(arena_size >> MPS_WORD_SHIFT));
|
|
}
|
|
|
|
size_t rnd_align(size_t min, size_t max)
|
|
{
|
|
unsigned log2min = sizelog2(min);
|
|
unsigned log2max = sizelog2(max);
|
|
Insist(min <= max);
|
|
Insist((size_t)1 << log2min == min);
|
|
Insist((size_t)1 << log2max == max);
|
|
if (log2min < log2max)
|
|
return min << (rnd() % (log2max - log2min + 1));
|
|
else
|
|
return min;
|
|
}
|
|
|
|
double rnd_pause_time(void)
|
|
{
|
|
double t = rnd_double();
|
|
if (t == 0.0)
|
|
return HUGE_VAL; /* Would prefer to use INFINITY but it's not in C89. */
|
|
else
|
|
return 1 / t - 1;
|
|
}
|
|
|
|
rnd_state_t rnd_seed(void)
|
|
{
|
|
/* Initialize seed based on seconds since epoch and on processor
|
|
* cycle count. */
|
|
EventClock t2;
|
|
EVENT_CLOCK(t2);
|
|
return 1 + ((unsigned long)time(NULL) + (unsigned long)t2) % (R_m - 1);
|
|
}
|
|
|
|
|
|
/* randomize -- randomize the generator, or initialize to replay
|
|
*
|
|
* There have been 3 versions of the rnd-states reported by this
|
|
* function:
|
|
*
|
|
* 1. before RHSK got his hands on rnd(), ie. pre-2008. These seed
|
|
* values are not currently supported, but it might be easy to
|
|
* add support.
|
|
*
|
|
* 2. v2 states: the published "seed" (state) value was the seed
|
|
* *before* the 10 rnds to churn up and separate nearby values
|
|
* from time(). This was unfortunate: you can't write a rnd_state
|
|
* getter, because it would have to go 10 steps backwards, and
|
|
* that's impossible.
|
|
* (2008..2010-03-22)
|
|
*
|
|
* 3. v3 states: the published state is the state *after* all
|
|
* initialization is complete. Therefore you can easily store and
|
|
* re-use the published state. (From 2010-03-22, changelist
|
|
* 170093).
|
|
*/
|
|
|
|
void randomize(int argc, char *argv[])
|
|
{
|
|
int n;
|
|
unsigned long seed0;
|
|
|
|
if (argc > 1) {
|
|
n = sscanf(argv[1], "%lu", &seed0);
|
|
Insist(n == 1);
|
|
printf("%s: randomize(): resetting initial state (v3) to: %lu.\n",
|
|
argv[0], seed0);
|
|
rnd_state_set(seed0);
|
|
} else {
|
|
seed0 = rnd_seed();
|
|
printf("%s: randomize(): choosing initial state (v3): %lu.\n",
|
|
argv[0], seed0);
|
|
rnd_state_set(seed0);
|
|
}
|
|
(void)fflush(stdout); /* ensure seed is not lost in case of failure */
|
|
}
|
|
|
|
unsigned long rnd_state(void)
|
|
{
|
|
return seed;
|
|
}
|
|
|
|
void rnd_state_set(unsigned long seed0)
|
|
{
|
|
Insist(seed0 < R_m);
|
|
Insist(seed0 != 0);
|
|
seed = seed0;
|
|
|
|
rnd_verify(0);
|
|
Insist(seed == seed0);
|
|
}
|
|
|
|
/* rnd_state_set_2 -- legacy support for v2 rnd states
|
|
*
|
|
* In v2, the published "seed" (state) value was the seed *before*
|
|
* the 10 rnds to churn up and separate nearby values from time().
|
|
*
|
|
* Set the seed, then convert it to a v3 state by doing those 10 rnds.
|
|
*/
|
|
void rnd_state_set_v2(unsigned long seed0_v2)
|
|
{
|
|
int i;
|
|
unsigned long seed0;
|
|
|
|
rnd_state_set(seed0_v2);
|
|
for(i = 0; i < 10; i += 1) {
|
|
(void)rnd();
|
|
}
|
|
|
|
seed0 = rnd_state();
|
|
printf("rnd_state_set_v2(): seed0_v2 = %lu, converted to state_v3 = %lu.\n", seed0_v2, seed0);
|
|
rnd_state_set(seed0);
|
|
}
|
|
|
|
|
|
/* res_strings -- human readable MPS result codes */
|
|
|
|
static struct {
|
|
const char *ident;
|
|
const char *doc;
|
|
} const res_strings[] = {
|
|
#define RES_STRINGS_ROW(X, ident, doc) {#ident, doc},
|
|
_mps_RES_ENUM(RES_STRINGS_ROW, X)
|
|
};
|
|
|
|
|
|
/* verror -- die with message */
|
|
|
|
ATTRIBUTE_FORMAT((printf, 1, 0))
|
|
void verror(const char *format, va_list args)
|
|
{
|
|
(void)fflush(stdout); /* synchronize */
|
|
(void)vfprintf(stderr, format, args);
|
|
(void)fprintf(stderr, "\n");
|
|
(void)fflush(stderr); /* make sure the message is output */
|
|
mps_telemetry_flush();
|
|
/* On Windows, the abort signal pops up a dialog box. This suspends
|
|
* the test suite until a button is pressed, which is not acceptable
|
|
* for offline testing, so if the MPS_TESTLIB_NOABORT environment
|
|
* variable is set, then the test case exits instead of aborting.
|
|
*/
|
|
if (getenv("MPS_TESTLIB_NOABORT")) {
|
|
exit(EXIT_FAILURE);
|
|
} else {
|
|
abort();
|
|
}
|
|
}
|
|
|
|
|
|
/* error -- die with message */
|
|
|
|
ATTRIBUTE_FORMAT((printf, 1, 2))
|
|
void error(const char *format, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
verror(format, args);
|
|
/* va_end(args); */ /* provokes "unreachable code" error from MSVC */
|
|
}
|
|
|
|
|
|
/* die_expect -- Test a return code, and exit on unexpected result */
|
|
|
|
void die_expect(mps_res_t res, mps_res_t expected, const char *s)
|
|
{
|
|
if (res != expected) {
|
|
if (0 <= res && (unsigned)res < NELEMS(res_strings))
|
|
error("\n%s: %s: %s\n", s, res_strings[res].ident, res_strings[res].doc);
|
|
else
|
|
error("\n%s: %d: unknown result code\n", s, res);
|
|
}
|
|
}
|
|
|
|
|
|
/* die -- Test a return code, and exit on error */
|
|
|
|
void die(mps_res_t res, const char *s)
|
|
{
|
|
die_expect(res, MPS_RES_OK, s);
|
|
}
|
|
|
|
|
|
/* cdie -- Test a C boolean, and exit on error */
|
|
|
|
void cdie(int res, const char *s)
|
|
{
|
|
if (!res) {
|
|
error("\n%s: %d\n", s, res);
|
|
}
|
|
}
|
|
|
|
|
|
/* assert_die -- always die on assertion */
|
|
|
|
void assert_die(const char *file, unsigned line, const char *condition)
|
|
{
|
|
error("%s:%u: MPS ASSERTION FAILED: %s\n", file, line, condition);
|
|
}
|
|
|
|
|
|
/* testlib_init -- install assertion handler and seed RNG */
|
|
|
|
void testlib_init(int argc, char *argv[])
|
|
{
|
|
(void)mps_lib_assert_fail_install(assert_die);
|
|
randomize(argc, argv);
|
|
}
|
|
|
|
|
|
/* C. COPYRIGHT AND LICENSE
|
|
*
|
|
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the
|
|
* distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|