1 /* -*- Mode: C; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
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 // -------------------------------------------------------------------------*/
12 #include "sjme/except.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
;
31 * Mock function to do type.
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
},
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
;
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
;
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
;
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
;
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
;
139 return SJME_ERROR_RESOURCE_NOT_FOUND
;
141 /* Open the stream. */
142 return sjme_stream_inputOpenMemory(pool
, outStream
,
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
)
153 sjme_mock_configWorkData data
;
154 sjme_mock_doType doType
;
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
;
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
);
204 return SJME_JNI_TRUE
;
207 sjme_pointer
sjme_mock_alloc(
208 sjme_attrInNotNull sjme_mock
* inState
,
209 sjme_attrInPositiveNonZero
size_t inLen
)
215 return sjme_dieP("No input state.");
218 if (sjme_error_is(sjme_alloc(inState
->allocPool
, inLen
,
220 return sjme_dieP("Could not allocate pointer in test pool.");
225 sjme_jboolean
sjme_mock_doNvmState(
226 sjme_attrInNotNull sjme_mock
* inState
,
227 sjme_attrInNotNull sjme_mock_configWorkData
* inData
)
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
;
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
;
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. */
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
;
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
]
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
;
349 /* Store the type onto the stack. */
350 stack
->order
[stack
->count
] = typeId
;
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
);
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;
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
;
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. */
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. */
472 library
->id
= data
->id
;
474 library
->id
= inData
->current
.indexType
+ 1;
476 /* Name of the library. */
477 if (data
->name
!= NULL
)
478 library
->name
= data
->name
;
481 if (sjme_error_is(sjme_alloc_format(inState
->allocPool
,
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
;
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
;
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. */
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.");
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.");
526 inState
->romLibraries
[inState
->numRomLibraries
++] = result
;
529 return SJME_JNI_TRUE
;
532 /* Synthetic resource access. */
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. */
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
),
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. */
599 if (sjme_error_is(writeFunctions
->initCache(
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
;
616 return SJME_JNI_TRUE
;