Merge from trunk.
[SquirrelJME.git] / nanocoat / tests / src / mock.c
blob2fb46b573ba3a76ceda70e8d70c0537c4ff4f94d
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 "mock.h"
13 #include "mock.jar.h"
14 #include "hello.txt.h"
15 #include "MockMain.class.h"
17 struct sjme_mock_configWorkData
19 /** The index type counts. */
20 sjme_jint indexTypeCount[SJME_NUM_MOCK_DO_TYPES];
22 /** The current run. */
23 sjme_mock_configWork current;
25 /** The next thread ID. */
26 sjme_jint nextThreadId;
29 /**
30 * Mock function to do type.
32 * @since 2023/11/11
34 struct
36 sjme_mock_doFunc func;
37 sjme_mock_doType type;
38 } sjme_mockFuncToType[SJME_NUM_MOCK_DO_TYPES] =
40 {sjme_mock_doNvmFrame,
41 SJME_MOCK_DO_TYPE_NVM_FRAME},
42 {sjme_mock_doNvmObject,
43 SJME_MOCK_DO_TYPE_NVM_OBJECT},
44 {sjme_mock_doNvmState,
45 SJME_MOCK_DO_TYPE_NVM_STATE},
46 {sjme_mock_doNvmThread,
47 SJME_MOCK_DO_TYPE_NVM_THREAD},
48 {sjme_mock_doRomLibrary,
49 SJME_MOCK_DO_TYPE_ROM_LIBRARY},
50 {sjme_mock_doRomMockLibrary,
51 SJME_MOCK_DO_TYPE_ROM_MOCK_LIBRARY},
52 {sjme_mock_doRomSuite,
53 SJME_MOCK_DO_TYPE_ROM_SUITE},
55 /* End. */
56 {NULL, SJME_MOCK_DO_TYPE_UNKNOWN}
59 static sjme_errorCode sjme_mock_defaultRomLibraryRawData(
60 sjme_attrInNotNull sjme_rom_library inLibrary,
61 sjme_attrOutNotNullBuf(length) sjme_pointer dest,
62 sjme_attrInPositive sjme_jint srcPos,
63 sjme_attrInPositive sjme_jint length)
65 sjme_mock_configDataRomLibrary* mock;
67 if (inLibrary == NULL || dest == NULL)
68 return SJME_ERROR_NULL_ARGUMENTS;
70 if (srcPos < 0 || length < 0)
71 return SJME_ERROR_INDEX_OUT_OF_BOUNDS;
73 /* Recover mock. */
74 mock = inLibrary->cache.common.frontEnd.data;
76 /* Double check size. */
77 if (srcPos + length < 0 || srcPos + length > mock->length)
78 return SJME_ERROR_INDEX_OUT_OF_BOUNDS;
80 /* There is no actual data here? */
81 if (mock->data == NULL)
82 return SJME_ERROR_ILLEGAL_STATE;
84 /* Just do a normal copy over. */
85 memmove(dest, (sjme_pointer)(((uintptr_t)mock->data) + srcPos), length);
86 return SJME_ERROR_NONE;
89 static sjme_errorCode sjme_mock_defaultRomLibraryRawSize(
90 sjme_attrInNotNull sjme_rom_library inLibrary,
91 sjme_attrOutNotNull sjme_jint* outSize)
93 sjme_mock_configDataRomLibrary* mock;
95 if (inLibrary == NULL || outSize == NULL)
96 return SJME_ERROR_NULL_ARGUMENTS;
98 /* Recover mock. */
99 mock = inLibrary->cache.common.frontEnd.data;
101 /* Is a simple set operation. */
102 *outSize = mock->length;
103 return SJME_ERROR_NONE;
106 static sjme_errorCode sjme_mock_defaultRomMockLibraryResourceStream(
107 sjme_attrInNotNull sjme_rom_library inLibrary,
108 sjme_attrOutNotNull sjme_stream_input* outStream,
109 sjme_attrInNotNull sjme_lpcstr resourceName)
111 sjme_alloc_pool* pool;
112 sjme_cpointer data;
113 sjme_jint len;
115 if (inLibrary == NULL || resourceName == NULL || outStream == NULL)
116 return SJME_ERROR_NULL_ARGUMENTS;
118 /* Which pool to allocate within? */
119 pool = inLibrary->cache.common.allocPool;
121 /* Debug. */
122 sjme_message("Looking for resource %s...", resourceName);
124 /* Depends on the resource name. */
125 if (0 == strcmp(resourceName, "hello.txt"))
127 data = hello_txt__bin;
128 len = hello_txt__len;
130 else if (0 == strcmp(resourceName, "MockMain.class"))
132 data = mockmain_class__bin;
133 len = mockmain_class__len;
136 /* Not found. */
137 else
138 return SJME_ERROR_RESOURCE_NOT_FOUND;
140 /* Open the stream. */
141 return sjme_stream_inputOpenMemory(pool, outStream,
142 data, len);
145 sjme_jboolean sjme_mock_act(
146 sjme_attrInNotNull sjme_test* inTest,
147 sjme_attrInNotNull sjme_mock* inState,
148 sjme_attrInNotNull const sjme_mock_configSet* inSet,
149 sjme_attrInValue sjme_jint special)
151 sjme_jint dx, i;
152 sjme_mock_configWorkData data;
153 sjme_mock_doType doType;
155 /* Check. */
156 if (inState == NULL || inSet == NULL)
157 return sjme_die("Null arguments.");
159 /* Use the testing pool. */
160 inState->allocPool = inTest->pool;
162 /* Initialize base data. */
163 memset(&data, 0, sizeof(data));
165 /* Go through each entry, stop at NULl. */
166 for (dx = 0; inSet->order[dx] != NULL; dx++)
168 /* Always wipe the current data so it is fresh. */
169 memset(&data.current, 0, sizeof(data.current));
171 /* This index is just a straight through. */
172 data.current.indexAll = dx;
174 /* Find the type for this function. */
175 doType = SJME_MOCK_DO_TYPE_UNKNOWN;
176 for (i = 0; i < SJME_NUM_MOCK_DO_TYPES; i++)
177 if (inSet->order[dx] == sjme_mockFuncToType[i].func)
179 doType = sjme_mockFuncToType[i].type;
180 break;
183 /* Not found? */
184 if (doType == SJME_MOCK_DO_TYPE_UNKNOWN)
185 return sjme_die("Could not find the type for do function.");
187 /* Increment up the index for this. */
188 data.current.type = doType;
189 data.current.indexType = data.indexTypeCount[doType]++;
190 data.current.special = special;
192 /* Run configuration function to initialize the data set. */
193 if (inSet->config != NULL)
194 if (!inSet->config(inState, &data.current))
195 return sjme_die("Configuration step failed at %d.", dx);
197 /* Call do function to perform whatever test initialization. */
198 if (!inSet->order[dx](inState, &data))
199 return sjme_die("Do failed at %d.", dx);
202 /* Successful. */
203 return SJME_JNI_TRUE;
206 sjme_pointer sjme_mock_alloc(
207 sjme_attrInNotNull sjme_mock* inState,
208 sjme_attrInPositiveNonZero size_t inLen)
210 sjme_pointer rv;
212 /* Check. */
213 if (inState == NULL)
214 return sjme_dieP("No input state.");
216 rv = NULL;
217 if (sjme_error_is(sjme_alloc(inState->allocPool, inLen,
218 &rv)))
219 return sjme_dieP("Could not allocate pointer in test pool.");
221 return rv;
224 sjme_jboolean sjme_mock_doNvmState(
225 sjme_attrInNotNull sjme_mock* inState,
226 sjme_attrInNotNull sjme_mock_configWorkData* inData)
228 sjme_nvm newState;
230 if (inState == NULL || inData == NULL)
231 return sjme_die("Null arguments.");
233 /* Allocate virtual machine state. */
234 newState = sjme_mock_alloc(inState,
235 sizeof(*inState->nvmState));
236 inState->nvmState = newState;
238 /* Store test state, as required for some tests. */
239 newState->common.frontEnd.data = inState;
241 /* Register any hooks? */
242 if (inData->current.data.nvmState.hooks != NULL)
243 newState->hooks = inData->current.data.nvmState.hooks;
245 /* Done. */
246 return SJME_JNI_TRUE;
249 sjme_jboolean sjme_mock_doNvmFrame(
250 sjme_attrInNotNull sjme_mock* inState,
251 sjme_attrInNotNull sjme_mock_configWorkData* inData)
253 sjme_jint threadIndex, treadMax, tallyLocals, stackBase, desireMaxLocals;
254 sjme_jint tallyStack, desireMaxStack, localIndex;
255 sjme_nvm_thread thread;
256 sjme_nvm_frame newFrame;
257 sjme_basicTypeId typeId;
258 sjme_nvm_frameTread* tread;
259 sjme_nvm_frameStack* stack;
260 sjme_nvm_frameLocalMap* localMap;
261 sjme_jbyte baseLocalAt[SJME_NUM_JAVA_TYPE_IDS];
263 if (inState == NULL || inData == NULL)
264 return sjme_die("Null arguments.");
266 /* Make sure the requested thread index is valid. */
267 threadIndex = inData->current.data.nvmFrame.threadIndex;
268 if (threadIndex < 0 || threadIndex >= SJME_MOCK_MAX_THREADS ||
269 inState->threads[threadIndex].nvmThread == NULL)
270 return sjme_die("Invalid thread index %d.", threadIndex);
272 /* Get the actual thread. */
273 thread = inState->threads[threadIndex].nvmThread;
275 /* Allocate new frame. */
276 newFrame = sjme_mock_alloc(inState, sizeof(*newFrame));
277 if (newFrame == NULL)
278 return sjme_die("Could not allocate frame.");
280 /* Correlate the frame index to the thread. */
281 newFrame->frameIndex = thread->numFrames;
282 thread->numFrames++;
284 /* Link in frame to the thread. */
285 newFrame->inThread = thread;
286 newFrame->parent = thread->top;
287 thread->top = newFrame;
289 /* Track tally of locals and stack for consistency. */
290 tallyLocals = 0;
291 tallyStack = 0;
293 /* Setup locals mapping. */
294 desireMaxLocals = inData->current.data.nvmFrame.maxLocals;
295 localMap = sjme_mock_alloc(inState,
296 SJME_SIZEOF_FRAME_LOCAL_MAP(desireMaxLocals));
297 localMap->max = desireMaxLocals;
299 /* Setup stack information. */
300 desireMaxStack = inData->current.data.nvmFrame.maxStack;
301 stack = sjme_mock_alloc(inState,
302 SJME_SIZEOF_FRAME_STACK(desireMaxStack));
303 newFrame->stack = stack;
304 stack->limit = desireMaxStack;
306 /* Remember to set the local mapping in the frame. */
307 newFrame->localMap = localMap;
309 /* Clear base local map set trackers. */
310 memset(baseLocalAt, 0, sizeof(baseLocalAt));
312 /* Need to initialize frame locals and stack? */
313 for (typeId = 0; typeId < SJME_NUM_JAVA_TYPE_IDS; typeId++)
315 /* Ignore if empty. */
316 treadMax = inData->current.data.nvmFrame.treads[typeId].max;
317 if (treadMax <= 0)
318 continue;
320 /* Allocate target tread. */
321 tread = sjme_mock_alloc(inState,
322 SJME_SIZEOF_FRAME_TREAD_VAR(typeId, treadMax));
323 newFrame->treads[typeId] = tread;
325 /* Setup stack base. */
326 stackBase = inData->current.data.nvmFrame.treads[typeId]
327 .stackBaseIndex;
328 if (stackBase < 0 || stackBase > treadMax)
329 return sjme_die("Invalid test stack base %d, outside range %d.",
330 stackBase, treadMax);
332 /* Local tally goes up by the stack base. */
333 tallyLocals += stackBase;
335 /* Tally number of stack items. */
336 tallyStack += treadMax - stackBase;
338 /* Setup other tread details. */
339 tread->stackBaseIndex = stackBase;
340 tread->count = stackBase;
341 tread->max = treadMax;
343 /* Fill in local mappings for a given tread. */
344 for (localIndex = 0; localIndex < stackBase; localIndex++)
345 localMap->maps[localIndex].to[typeId] = (sjme_jbyte)localIndex;
347 #if 0
348 /* Store the type onto the stack. */
349 stack->order[stack->count] = typeId;
350 stack->count++;
351 #endif
354 /* Consistency check. */
355 if (tallyLocals != desireMaxLocals)
356 return sjme_die("Calculated and desired locals invalid: %d != %d.",
357 tallyLocals, desireMaxLocals);
359 if (tallyStack != desireMaxStack)
360 return sjme_die("Calculated and desired stack invalid: %d != %d.",
361 tallyStack, desireMaxStack);
363 /* Done. */
364 return SJME_JNI_TRUE;
367 sjme_jboolean sjme_mock_doNvmObject(
368 sjme_attrInNotNull sjme_mock* inState,
369 sjme_attrInNotNull sjme_mock_configWorkData* inData)
371 sjme_jobject newObject;
373 if (inState == NULL || inData == NULL)
374 return sjme_die("Null arguments.");
376 /* Too many objects? */
377 if (inState->numObjects >= SJME_MOCK_MAX_OBJECTS)
378 sjme_die("Too many mock objects.");
380 /* Allocate new object. */
381 newObject = sjme_mock_alloc(inState, sizeof(*newObject));
382 inState->objects[inState->numObjects++] = newObject;
384 /* Initialize object details. */
385 newObject->refCount = 1;
387 /* Success. */
388 return SJME_JNI_TRUE;
391 sjme_jboolean sjme_mock_doNvmThread(
392 sjme_attrInNotNull sjme_mock* inState,
393 sjme_attrInNotNull sjme_mock_configWorkData* inData)
395 sjme_jint threadIndex;
396 sjme_nvm_thread newThread;
398 if (inState == NULL || inData == NULL)
399 return sjme_die("Null arguments.");
401 /* Mock has a limited set of threads for testing purposes. */
402 threadIndex = inState->numThreads;
403 if (threadIndex >= SJME_MOCK_MAX_THREADS)
404 return sjme_die("Too make mock threads.");
406 /* Allocate thread. */
407 newThread = sjme_mock_alloc(inState, sizeof(*newThread));
408 if (newThread == NULL)
409 return sjme_die("Could not allocate thread.");
411 /* Store in thread and bump up. */
412 newThread->threadId = ++inData->nextThreadId;
413 newThread->inState = inState->nvmState;
414 inState->threads[threadIndex].nvmThread = newThread;
416 /* Done. */
417 return SJME_JNI_TRUE;
420 sjme_jboolean sjme_mock_doRomLibrary(
421 sjme_attrInNotNull sjme_mock* inState,
422 sjme_attrInNotNull sjme_mock_configWorkData* inData)
424 sjme_jint libraryIndex;
425 sjme_rom_libraryBase* library;
426 sjme_mock_configDataRomLibrary* data;
427 sjme_rom_libraryFunctions* functions;
429 if (inState == NULL || inData == NULL)
430 return sjme_die("Null arguments.");
432 /* Index of the resultant library. */
433 libraryIndex = inState->numRomLibraries;
434 if (libraryIndex >= SJME_MOCK_MAX_ROM_LIBRARIES)
435 return sjme_die("Too many libraries.");
437 /* Allocate library. */
438 library = NULL;
439 if (sjme_error_is(sjme_alloc(inState->allocPool,
440 sizeof(*library), (void**)&library)) ||
441 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 (sjme_lpstr*)&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_errorCode error;
500 sjme_jint libraryIndex;
501 sjme_mock_configDataRomLibrary* data;
502 sjme_jboolean isJar;
503 sjme_rom_library result;
505 if (inState == NULL || inData == NULL)
506 return sjme_die("Null arguments.");
508 /* Is this a JAR or not? */
509 isJar = inData->current.data.romMockLibrary.isJar;
511 /* Using an actual JAR file? Just open an actual library. */
512 if (isJar)
514 /* Index of the resultant library. */
515 libraryIndex = inState->numRomLibraries;
516 if (libraryIndex >= SJME_MOCK_MAX_ROM_LIBRARIES)
517 return sjme_die("Too many libraries.");
519 /* Open it. */
520 result = NULL;
521 if (sjme_error_is(error = sjme_rom_libraryFromZipMemory(
522 inState->allocPool, &result, "mock.jar",
523 mock_jar__bin, mock_jar__len)) || result == NULL)
524 return sjme_die("Could not open library: %d", error);
526 /* Register it. */
527 inState->romLibraries[inState->numRomLibraries++] = result;
529 /* Success! */
530 return SJME_JNI_TRUE;
533 /* Synthetic resource access. */
534 else
536 /* Clear existing settings. */
537 data = &inData->current.data.romLibrary;
538 memset(data, 0, sizeof(*data));
540 /* Setup aliased mock library. */
541 data->name = "mock.jar";
543 data->functions.resourceStream =
544 sjme_mock_defaultRomMockLibraryResourceStream;
546 /* This is just an alias so call the other accordingly. */
547 return sjme_mock_doRomLibrary(inState, inData);
551 sjme_jboolean sjme_mock_doRomSuite(
552 sjme_attrInNotNull sjme_mock* inState,
553 sjme_attrInNotNull sjme_mock_configWorkData* inData)
555 sjme_jint suiteIndex;
556 sjme_rom_suite suite;
557 sjme_rom_suiteFunctions* writeFunctions;
558 sjme_mock_configDataRomSuite* suiteData;
560 if (inState == NULL || inData == NULL)
561 return sjme_die("Null arguments.");
563 /* Too many suites declared already? */
564 suiteIndex = inState->numRomSuites;
565 if (suiteIndex >= SJME_MOCK_MAX_ROM_SUITES)
566 return sjme_die("Too many ROM suites.");
568 /* Allocate suite. */
569 suite = NULL;
570 if (sjme_error_is(sjme_alloc(inState->allocPool,
571 sizeof(*suite), (void**)&suite)) || suite == NULL)
572 return sjme_die("Could not allocate suite.");
574 /* Quicker this way... */
575 suiteData = &inData->current.data.romSuite;
577 /* Seed front end data. */
578 suite->cache.common.frontEnd.data = inState;
580 /* Copy suite functions. */
581 suite->functions = NULL;
582 if (sjme_error_is(sjme_alloc_copy(inState->allocPool,
583 sizeof(*suite->functions),
584 (void**)&suite->functions,
585 &suiteData->functions)) ||
586 suite->functions == NULL)
587 return sjme_die("Could not copy functions.");
589 /* Set front end to the test state. */
590 writeFunctions = (sjme_rom_suiteFunctions*)suite->functions;
592 /* If there is no cache init, just initialize it to something... */
593 if (writeFunctions->init == NULL)
594 memset(&suite->cache, 0, sizeof(suite->cache));
596 /* Otherwise call the initializer. */
597 else
599 if (sjme_error_is(writeFunctions->init(
600 suite, NULL)))
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;