Move cleanup handler elsewhere; Add sjme_alloc_copyWeak to copy data as a weak reference.
[SquirrelJME.git] / nanocoat / tests / src / mock.c
blob62701f53c69e7e6342519d764bcf2000170f9f91
1 /* -*- Mode: C; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
3 // SquirrelJME
4 // Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
5 // ---------------------------------------------------------------------------
6 // SquirrelJME is under the Mozilla Public License Version 2.0.
7 // See license.mkd for licensing and copyright information.
8 // -------------------------------------------------------------------------*/
10 #include <string.h>
12 #include "sjme/except.h"
13 #include "mock.h"
14 #include "mock.jar.h"
15 #include "hello.txt.h"
16 #include "MockMain.class.h"
18 struct sjme_mock_configWorkData
20 /** The index type counts. */
21 sjme_jint indexTypeCount[SJME_NUM_MOCK_DO_TYPES];
23 /** The current run. */
24 sjme_mock_configWork current;
26 /** The next thread ID. */
27 sjme_jint nextThreadId;
30 /**
31 * Mock function to do type.
33 * @since 2023/11/11
35 struct
37 sjme_mock_doFunc func;
38 sjme_mock_doType type;
39 } sjme_mockFuncToType[SJME_NUM_MOCK_DO_TYPES] =
41 {sjme_mock_doNvmFrame,
42 SJME_MOCK_DO_TYPE_NVM_FRAME},
43 {sjme_mock_doNvmObject,
44 SJME_MOCK_DO_TYPE_NVM_OBJECT},
45 {sjme_mock_doNvmState,
46 SJME_MOCK_DO_TYPE_NVM_STATE},
47 {sjme_mock_doNvmThread,
48 SJME_MOCK_DO_TYPE_NVM_THREAD},
49 {sjme_mock_doRomLibrary,
50 SJME_MOCK_DO_TYPE_ROM_LIBRARY},
51 {sjme_mock_doRomMockLibrary,
52 SJME_MOCK_DO_TYPE_ROM_MOCK_LIBRARY},
53 {sjme_mock_doRomSuite,
54 SJME_MOCK_DO_TYPE_ROM_SUITE},
56 /* End. */
57 {NULL, SJME_MOCK_DO_TYPE_UNKNOWN}
60 static sjme_errorCode sjme_mock_defaultRomLibraryRawData(
61 sjme_attrInNotNull sjme_rom_library inLibrary,
62 sjme_attrOutNotNullBuf(length) sjme_pointer dest,
63 sjme_attrInPositive sjme_jint srcPos,
64 sjme_attrInPositive sjme_jint length)
66 sjme_mock_configDataRomLibrary* mock;
68 if (inLibrary == NULL || dest == NULL)
69 return SJME_ERROR_NULL_ARGUMENTS;
71 if (srcPos < 0 || length < 0)
72 return SJME_ERROR_INDEX_OUT_OF_BOUNDS;
74 /* Recover mock. */
75 mock = inLibrary->cache.common.frontEnd.data;
77 /* Double check size. */
78 if (srcPos + length < 0 || srcPos + length > mock->length)
79 return SJME_ERROR_INDEX_OUT_OF_BOUNDS;
81 /* There is no actual data here? */
82 if (mock->data == NULL)
83 return SJME_ERROR_ILLEGAL_STATE;
85 /* Just do a normal copy over. */
86 memmove(dest, (sjme_pointer)(((uintptr_t)mock->data) + srcPos), length);
87 return SJME_ERROR_NONE;
90 static sjme_errorCode sjme_mock_defaultRomLibraryRawSize(
91 sjme_attrInNotNull sjme_rom_library inLibrary,
92 sjme_attrOutNotNull sjme_jint* outSize)
94 sjme_mock_configDataRomLibrary* mock;
96 if (inLibrary == NULL || outSize == NULL)
97 return SJME_ERROR_NULL_ARGUMENTS;
99 /* Recover mock. */
100 mock = inLibrary->cache.common.frontEnd.data;
102 /* Is a simple set operation. */
103 *outSize = mock->length;
104 return SJME_ERROR_NONE;
107 static sjme_errorCode sjme_mock_defaultRomMockLibraryResourceStream(
108 sjme_attrInNotNull sjme_rom_library inLibrary,
109 sjme_attrOutNotNull sjme_stream_input* outStream,
110 sjme_attrInNotNull sjme_lpcstr resourceName)
112 sjme_alloc_pool* pool;
113 sjme_cpointer data;
114 sjme_jint len;
116 if (inLibrary == NULL || resourceName == NULL || outStream == NULL)
117 return SJME_ERROR_NULL_ARGUMENTS;
119 /* Which pool to allocate within? */
120 pool = inLibrary->cache.common.allocPool;
122 /* Debug. */
123 sjme_message("Looking for resource %s...", resourceName);
125 /* Depends on the resource name. */
126 if (0 == strcmp(resourceName, "hello.txt"))
128 data = hello_txt__bin;
129 len = hello_txt__len;
131 else if (0 == strcmp(resourceName, "MockMain.class"))
133 data = mockmain_class__bin;
134 len = mockmain_class__len;
137 /* Not found. */
138 else
139 return SJME_ERROR_RESOURCE_NOT_FOUND;
141 /* Open the stream. */
142 return sjme_stream_inputOpenMemory(pool, outStream,
143 data, len);
146 sjme_jboolean sjme_mock_act(
147 sjme_attrInNotNull sjme_test* inTest,
148 sjme_attrInNotNull sjme_mock* inState,
149 sjme_attrInNotNull const sjme_mock_configSet* inSet,
150 sjme_attrInValue sjme_jint special)
152 sjme_jint dx, i;
153 sjme_mock_configWorkData data;
154 sjme_mock_doType doType;
156 /* Check. */
157 if (inState == NULL || inSet == NULL)
158 return sjme_die("Null arguments.");
160 /* Use the testing pool. */
161 inState->allocPool = inTest->pool;
163 /* Initialize base data. */
164 memset(&data, 0, sizeof(data));
166 /* Go through each entry, stop at NULl. */
167 for (dx = 0; inSet->order[dx] != NULL; dx++)
169 /* Always wipe the current data so it is fresh. */
170 memset(&data.current, 0, sizeof(data.current));
172 /* This index is just a straight through. */
173 data.current.indexAll = dx;
175 /* Find the type for this function. */
176 doType = SJME_MOCK_DO_TYPE_UNKNOWN;
177 for (i = 0; i < SJME_NUM_MOCK_DO_TYPES; i++)
178 if (inSet->order[dx] == sjme_mockFuncToType[i].func)
180 doType = sjme_mockFuncToType[i].type;
181 break;
184 /* Not found? */
185 if (doType == SJME_MOCK_DO_TYPE_UNKNOWN)
186 return sjme_die("Could not find the type for do function.");
188 /* Increment up the index for this. */
189 data.current.type = doType;
190 data.current.indexType = data.indexTypeCount[doType]++;
191 data.current.special = special;
193 /* Run configuration function to initialize the data set. */
194 if (inSet->config != NULL)
195 if (!inSet->config(inState, &data.current))
196 return sjme_die("Configuration step failed at %d.", dx);
198 /* Call do function to perform whatever test initialization. */
199 if (!inSet->order[dx](inState, &data))
200 return sjme_die("Do failed at %d.", dx);
203 /* Successful. */
204 return SJME_JNI_TRUE;
207 sjme_pointer sjme_mock_alloc(
208 sjme_attrInNotNull sjme_mock* inState,
209 sjme_attrInPositiveNonZero size_t inLen)
211 sjme_pointer rv;
213 /* Check. */
214 if (inState == NULL)
215 return sjme_dieP("No input state.");
217 rv = NULL;
218 if (sjme_error_is(sjme_alloc(inState->allocPool, inLen,
219 &rv)))
220 return sjme_dieP("Could not allocate pointer in test pool.");
222 return rv;
225 sjme_jboolean sjme_mock_doNvmState(
226 sjme_attrInNotNull sjme_mock* inState,
227 sjme_attrInNotNull sjme_mock_configWorkData* inData)
229 sjme_nvm newState;
231 if (inState == NULL || inData == NULL)
232 return sjme_die("Null arguments.");
234 /* Allocate virtual machine state. */
235 newState = sjme_mock_alloc(inState,
236 sizeof(*inState->nvmState));
237 inState->nvmState = newState;
239 /* Store test state, as required for some tests. */
240 newState->frontEnd.data = inState;
242 /* Register any hooks? */
243 if (inData->current.data.nvmState.hooks != NULL)
244 newState->hooks = inData->current.data.nvmState.hooks;
246 /* Done. */
247 return SJME_JNI_TRUE;
250 sjme_jboolean sjme_mock_doNvmFrame(
251 sjme_attrInNotNull sjme_mock* inState,
252 sjme_attrInNotNull sjme_mock_configWorkData* inData)
254 sjme_jint threadIndex, treadMax, tallyLocals, stackBase, desireMaxLocals;
255 sjme_jint tallyStack, desireMaxStack, localIndex;
256 sjme_nvm_thread thread;
257 sjme_nvm_frame newFrame;
258 sjme_basicTypeId typeId;
259 sjme_nvm_frameTread* tread;
260 sjme_nvm_frameStack* stack;
261 sjme_nvm_frameLocalMap* localMap;
262 sjme_jbyte baseLocalAt[SJME_NUM_JAVA_TYPE_IDS];
264 if (inState == NULL || inData == NULL)
265 return sjme_die("Null arguments.");
267 /* Make sure the requested thread index is valid. */
268 threadIndex = inData->current.data.nvmFrame.threadIndex;
269 if (threadIndex < 0 || threadIndex >= SJME_MOCK_MAX_THREADS ||
270 inState->threads[threadIndex].nvmThread == NULL)
271 return sjme_die("Invalid thread index %d.", threadIndex);
273 /* Get the actual thread. */
274 thread = inState->threads[threadIndex].nvmThread;
276 /* Allocate new frame. */
277 newFrame = sjme_mock_alloc(inState, sizeof(*newFrame));
278 if (newFrame == NULL)
279 return sjme_die("Could not allocate frame.");
281 /* Correlate the frame index to the thread. */
282 newFrame->frameIndex = thread->numFrames;
283 thread->numFrames++;
285 /* Link in frame to the thread. */
286 newFrame->inThread = thread;
287 newFrame->parent = thread->top;
288 thread->top = newFrame;
290 /* Track tally of locals and stack for consistency. */
291 tallyLocals = 0;
292 tallyStack = 0;
294 /* Setup locals mapping. */
295 desireMaxLocals = inData->current.data.nvmFrame.maxLocals;
296 localMap = sjme_mock_alloc(inState,
297 SJME_SIZEOF_FRAME_LOCAL_MAP(desireMaxLocals));
298 localMap->max = desireMaxLocals;
300 /* Setup stack information. */
301 desireMaxStack = inData->current.data.nvmFrame.maxStack;
302 stack = sjme_mock_alloc(inState,
303 SJME_SIZEOF_FRAME_STACK(desireMaxStack));
304 newFrame->stack = stack;
305 stack->limit = desireMaxStack;
307 /* Remember to set the local mapping in the frame. */
308 newFrame->localMap = localMap;
310 /* Clear base local map set trackers. */
311 memset(baseLocalAt, 0, sizeof(baseLocalAt));
313 /* Need to initialize frame locals and stack? */
314 for (typeId = 0; typeId < SJME_NUM_JAVA_TYPE_IDS; typeId++)
316 /* Ignore if empty. */
317 treadMax = inData->current.data.nvmFrame.treads[typeId].max;
318 if (treadMax <= 0)
319 continue;
321 /* Allocate target tread. */
322 tread = sjme_mock_alloc(inState,
323 SJME_SIZEOF_FRAME_TREAD_VAR(typeId, treadMax));
324 newFrame->treads[typeId] = tread;
326 /* Setup stack base. */
327 stackBase = inData->current.data.nvmFrame.treads[typeId]
328 .stackBaseIndex;
329 if (stackBase < 0 || stackBase > treadMax)
330 return sjme_die("Invalid test stack base %d, outside range %d.",
331 stackBase, treadMax);
333 /* Local tally goes up by the stack base. */
334 tallyLocals += stackBase;
336 /* Tally number of stack items. */
337 tallyStack += treadMax - stackBase;
339 /* Setup other tread details. */
340 tread->stackBaseIndex = stackBase;
341 tread->count = stackBase;
342 tread->max = treadMax;
344 /* Fill in local mappings for a given tread. */
345 for (localIndex = 0; localIndex < stackBase; localIndex++)
346 localMap->maps[localIndex].to[typeId] = (sjme_jbyte)localIndex;
348 #if 0
349 /* Store the type onto the stack. */
350 stack->order[stack->count] = typeId;
351 stack->count++;
352 #endif
355 /* Consistency check. */
356 if (tallyLocals != desireMaxLocals)
357 return sjme_die("Calculated and desired locals invalid: %d != %d.",
358 tallyLocals, desireMaxLocals);
360 if (tallyStack != desireMaxStack)
361 return sjme_die("Calculated and desired stack invalid: %d != %d.",
362 tallyStack, desireMaxStack);
364 /* Done. */
365 return SJME_JNI_TRUE;
368 sjme_jboolean sjme_mock_doNvmObject(
369 sjme_attrInNotNull sjme_mock* inState,
370 sjme_attrInNotNull sjme_mock_configWorkData* inData)
372 sjme_jobject newObject;
374 if (inState == NULL || inData == NULL)
375 return sjme_die("Null arguments.");
377 /* Too many objects? */
378 if (inState->numObjects >= SJME_MOCK_MAX_OBJECTS)
379 sjme_die("Too many mock objects.");
381 /* Allocate new object. */
382 newObject = sjme_mock_alloc(inState, sizeof(*newObject));
383 inState->objects[inState->numObjects++] = newObject;
385 /* Initialize object details. */
386 newObject->refCount = 1;
388 /* Success. */
389 return SJME_JNI_TRUE;
392 sjme_jboolean sjme_mock_doNvmThread(
393 sjme_attrInNotNull sjme_mock* inState,
394 sjme_attrInNotNull sjme_mock_configWorkData* inData)
396 sjme_jint threadIndex;
397 sjme_nvm_thread newThread;
399 if (inState == NULL || inData == NULL)
400 return sjme_die("Null arguments.");
402 /* Mock has a limited set of threads for testing purposes. */
403 threadIndex = inState->numThreads;
404 if (threadIndex >= SJME_MOCK_MAX_THREADS)
405 return sjme_die("Too make mock threads.");
407 /* Allocate thread. */
408 newThread = sjme_mock_alloc(inState, sizeof(*newThread));
409 if (newThread == NULL)
410 return sjme_die("Could not allocate thread.");
412 /* Store in thread and bump up. */
413 newThread->threadId = ++inData->nextThreadId;
414 newThread->inState = inState->nvmState;
415 inState->threads[threadIndex].nvmThread = newThread;
417 /* Done. */
418 return SJME_JNI_TRUE;
421 sjme_jboolean sjme_mock_doRomLibrary(
422 sjme_attrInNotNull sjme_mock* inState,
423 sjme_attrInNotNull sjme_mock_configWorkData* inData)
425 sjme_jint libraryIndex;
426 sjme_rom_libraryBase* library;
427 sjme_mock_configDataRomLibrary* data;
428 sjme_rom_libraryFunctions* functions;
430 if (inState == NULL || inData == NULL)
431 return sjme_die("Null arguments.");
433 /* Index of the resultant library. */
434 libraryIndex = inState->numRomLibraries;
435 if (libraryIndex >= SJME_MOCK_MAX_ROM_LIBRARIES)
436 return sjme_die("Too many libraries.");
438 /* Allocate library. */
439 library = NULL;
440 if (sjme_error_is(sjme_alloc(inState->allocPool,
441 sizeof(*library), &library)) || library == NULL)
442 return sjme_die("Could not allocate library.");
444 /* Make a copy of the input data to be used as front end specific data. */
445 library->cache.common.frontEnd.data = NULL;
446 if (sjme_error_is(sjme_alloc_copy(inState->allocPool,
447 sizeof(inData->current.data.romLibrary),
448 &library->cache.common.frontEnd.data,
449 &inData->current.data.romLibrary)) ||
450 library->cache.common.frontEnd.data == NULL)
451 return sjme_die("Could not copy data.");
453 /* Make sure the pool is set, otherwise other functions will not work. */
454 library->cache.common.allocPool = inState->allocPool;
456 /* Use the copied data instead. */
457 data = library->cache.common.frontEnd.data;
459 /* Setup baseline mock functions, if none are set for some. */
460 library->functions = &data->functions;
461 functions = &data->functions;
463 /* Generic raw data access? */
464 if (functions->rawData == NULL || functions->rawSize == NULL)
466 functions->rawData = sjme_mock_defaultRomLibraryRawData;
467 functions->rawSize = sjme_mock_defaultRomLibraryRawSize;
470 /* ID of the library. */
471 if (data->id != 0)
472 library->id = data->id;
473 else
474 library->id = inData->current.indexType + 1;
476 /* Name of the library. */
477 if (data->name != NULL)
478 library->name = data->name;
479 else
481 if (sjme_error_is(sjme_alloc_format(inState->allocPool,
482 &library->name,
483 "unnamed%d.jar", (int)(inData->current.indexType + 1))) ||
484 library->name == NULL)
485 return sjme_die("Could not set default library name.");
488 /* Store in mock data. */
489 inState->romLibraries[inState->numRomLibraries++] = library;
491 /* Success! */
492 return SJME_JNI_TRUE;
495 sjme_jboolean sjme_mock_doRomMockLibrary(
496 sjme_attrInNotNull sjme_mock* inState,
497 sjme_attrInNotNull sjme_mock_configWorkData* inData)
499 sjme_jint libraryIndex;
500 sjme_mock_configDataRomLibrary* data;
501 sjme_jboolean isJar;
502 sjme_rom_library result;
504 if (inState == NULL || inData == NULL)
505 return sjme_die("Null arguments.");
507 /* Is this a JAR or not? */
508 isJar = inData->current.data.romMockLibrary.isJar;
510 /* Using an actual JAR file? Just open an actual library. */
511 if (isJar)
513 /* Index of the resultant library. */
514 libraryIndex = inState->numRomLibraries;
515 if (libraryIndex >= SJME_MOCK_MAX_ROM_LIBRARIES)
516 return sjme_die("Too many libraries.");
518 /* Open it. */
519 result = NULL;
520 if (sjme_error_is(sjme_rom_libraryFromZipMemory(
521 inState->allocPool, &result,
522 mock_jar__bin, mock_jar__len)) || result == NULL)
523 return sjme_die("Could not open library.");
525 /* Register it. */
526 inState->romLibraries[inState->numRomLibraries++] = result;
528 /* Success! */
529 return SJME_JNI_TRUE;
532 /* Synthetic resource access. */
533 else
535 /* Clear existing settings. */
536 data = &inData->current.data.romLibrary;
537 memset(data, 0, sizeof(*data));
539 /* Setup aliased mock library. */
540 data->name = "mock.jar";
542 data->functions.resourceStream =
543 sjme_mock_defaultRomMockLibraryResourceStream;
545 /* This is just an alias so call the other accordingly. */
546 return sjme_mock_doRomLibrary(inState, inData);
550 sjme_jboolean sjme_mock_doRomSuite(
551 sjme_attrInNotNull sjme_mock* inState,
552 sjme_attrInNotNull sjme_mock_configWorkData* inData)
554 sjme_jint suiteIndex;
555 sjme_rom_suite suite;
556 sjme_rom_suiteFunctions* writeFunctions;
557 sjme_mock_configDataRomSuite* suiteData;
559 if (inState == NULL || inData == NULL)
560 return sjme_die("Null arguments.");
562 /* Too many suites declared already? */
563 suiteIndex = inState->numRomSuites;
564 if (suiteIndex >= SJME_MOCK_MAX_ROM_SUITES)
565 return sjme_die("Too many ROM suites.");
567 /* Allocate suite. */
568 suite = NULL;
569 if (sjme_error_is(sjme_alloc(inState->allocPool,
570 sizeof(*suite), &suite)) || suite == NULL)
571 return sjme_die("Could not allocate suite.");
573 /* Quicker this way... */
574 suiteData = &inData->current.data.romSuite;
576 /* Seed front end data. */
577 suite->cache.common.frontEnd.data = inState;
579 /* Copy suite functions. */
580 suite->functions = NULL;
581 if (sjme_error_is(sjme_alloc_copy(inState->allocPool,
582 sizeof(*suite->functions),
583 &suite->functions,
584 &suiteData->functions)) ||
585 suite->functions == NULL)
586 return sjme_die("Could not copy functions.");
588 /* Set front end to the test state. */
589 writeFunctions = (sjme_rom_suiteFunctions*)suite->functions;
591 /* If there is no cache init, just initialize it to something... */
592 if (writeFunctions->initCache == NULL)
593 memset(&suite->cache, 0,
594 SJME_SIZEOF_SUITE_CACHE_N(writeFunctions->uncommonTypeSize));
596 /* Otherwise call the initializer. */
597 else
599 if (sjme_error_is(writeFunctions->initCache(
600 suite)))
601 return sjme_die("Could not initialize suite via cache init.");
604 /* Set the allocation pool to use if not set. */
605 if (suite->cache.common.allocPool == NULL)
606 suite->cache.common.allocPool = inState->allocPool;
608 /* Is there a pre-cache used for libraries? */
609 if (suiteData->cacheLibraries != NULL)
610 suite->cache.libraries = suiteData->cacheLibraries;
612 /* Place finalized suite down. */
613 inState->romSuites[inState->numRomSuites] = suite;
615 /* Continue on. */
616 return SJME_JNI_TRUE;