@Override
	public final void runTest() {

		String testGroupName = getName();

		// TODO: HACK! Must strip "test" prefix which was added to original group name
		// to facilitate running single test in Eclipse
		if (testGroupName != null) {
			if (testGroupName.startsWith(TEST_PREFIX)) {
				testGroupName = testGroupName.substring(TEST_PREFIX.length());
			}
			else {
				fail("Expected test name to start with " + TEST_PREFIX);
			}
		}

		if (testGroupName == null || testGroupName.length() == 0) {
			fail("Empty test group name");
		}

		PCodeTestGroup testGroup = testGroupMap.get(testGroupName);
		assertNotNull("TestGroup not found for '" + testGroupName + "'", testGroup);

		log(testGroup, "Prepare for executing group test '" + testGroup.testGroupName + "' at: " +
			testGroup.functionEntryPtr.toString(true));

		log(testGroup,
			"Loading test binary: " + testGroup.mainTestControlBlock.testFile.fileReferencePath);

		log(testGroup, "Using cached program: " + testGroup.mainTestControlBlock.cachedProgramPath);

		Program program = null;
		EmulatorTestRunner testRunner = null;
		try {
			program = getGzfProgram(testGroup.mainTestControlBlock.cachedProgramPath);

			assertNotNull(
				"Failed to open test program: " + testGroup.mainTestControlBlock.cachedProgramPath,
				program);

			if (program.isChanged()) {
				// update cached program
				String fpath = testGroup.mainTestControlBlock.testFile.fileReferencePath;
				Msg.info(ProcessorEmulatorTestAdapter.class,
					"Updating cached gzf file following program upgrade: " + fpath);
				File gzfFile = new File(resourcesCacheDir, fpath + GZF_FILE_EXT);
				env.getGhidraProject().saveAsPackedFile(program, gzfFile, true);
				if (!gzfFile.exists()) {
					throw new IOException("Failed to cache gzf file: " + gzfFile);
				}
			}

			assertFalse(
				"Program contains severe disassembly/relocation errors: " +
					testGroup.mainTestControlBlock.cachedProgramPath,
				logData.testResults.summaryHasRelocationErrors ||
					logData.testResults.summaryHasDisassemblyErrors);

			testRunner = new EmulatorTestRunner(program, testGroup, this);

			// Clear hard-coded stack pointer value established by SimpleProgramLoadData.
			// If not set by initializeState checkStackPointerValue will attempt to initialize
			// TODO: not required if use of SimpleProgramLoadData eliminated
			testRunner.setRegister(compilerSpec.getStackPointer().getName(), 0);

			// Initialize context register based upon function entry within listing
			Address address = EmulatorTestRunner.alignAddress(testGroup.functionEntryPtr,
				language.getInstructionAlignment());
			ProgramContext programContext = program.getProgramContext();
			Register contextRegister = programContext.getBaseContextRegister();
			if (contextRegister != null) {
				testRunner.setContextRegister(
					programContext.getRegisterValue(contextRegister, address));
			}

			initializeState(testRunner, program);

			checkStackPointerValue(testRunner);

			int totalExpectedAsserts = 0;
			int numSubTests = testGroup.controlBlock.getNumberFunctions();
			for (int i = 1; i < numSubTests; i++) {
				FunctionInfo functionInfo = testGroup.controlBlock.getFunctionInfo(i);
				String name = functionInfo.functionName;
				logData.testResults.declareTest(testGroup.testGroupName, name,
					functionInfo.numberOfAsserts);
				totalExpectedAsserts += functionInfo.numberOfAsserts;
			}

			// Initialize pass/fail counts at runtime to detect severe failure
			testGroup.mainTestControlBlock.setNumberPassed(testRunner, Integer.MIN_VALUE);
			testGroup.mainTestControlBlock.setNumberFailed(testRunner, Integer.MIN_VALUE);
			testGroup.mainTestControlBlock.clearNumberIgnored();

			boolean done;
			if (traceDisabled) {
				done = testRunner.execute(EXECUTION_TIMEOUT_MS, TaskMonitor.DUMMY);
			}
			else {
				done = testRunner.executeSingleStep(MAX_EXECUTION_STEPS);
			}

			int pass = testGroup.mainTestControlBlock.getNumberPassed(testRunner);
			int ignoredPassed = testGroup.mainTestControlBlock.getNumberPassedIgnored();
			int ignoredFailed = testGroup.mainTestControlBlock.getNumberFailedIgnored();
			int callOtherErrors = testRunner.getCallOtherErrors();
			int fail = testGroup.mainTestControlBlock.getNumberFailed(testRunner);

			if (pass < 0 || fail < 0) {
				failTest(testRunner,
					"ERROR Invalid pass/fail counts - test may not have run properly or improper TestInfo structure updates occurred: pass " +
						pass + " fail " + fail);
			}

			pass -= ignoredPassed;
			pass -= callOtherErrors;
			fail -= ignoredFailed;

			String passFailText = "Passed: " + pass + " Ignored: " +
				(ignoredFailed + ignoredPassed) + " Failed: " + fail;
			if (callOtherErrors != 0) {
				passFailText += " Passed(w/CALLOTHER): " + callOtherErrors;
			}
			passFailText += " Expected Assertions: " + totalExpectedAsserts;
			log(testGroup, passFailText);

			boolean hasFailures = false;
			List<String> testFailures = testGroup.getTestFailures();
			if (!testFailures.isEmpty()) {
				if (!traceDisabled) {
					log(testGroup, "TEST FAILURE SUMMARY:");
				}
				for (String testFailure : testFailures) {
					if (!traceDisabled) {
						log(testGroup, " >>> " + testFailure);
					}
					// failure is any entry not marked as ignored
					hasFailures |= (testFailure.indexOf(PCodeTestGroup.IGNORED_TAG) < 0);
				}
			}

			if (!done) {
				StringBuilder msg = new StringBuilder("ERROR Test execution failed");

				// TODO: not currently set until assert function has been emulated
				//String lastFunction = testGroup.mainTestControlBlock.getLastFunctionName(testRunner);

				String emuError = testRunner.getEmuError();
				if (emuError != null) {
					msg.append(" - ");
					msg.append(emuError);
				}
				Address pcAddr = testRunner.getCurrentAddress();
				String pcStr = Long.toHexString(pcAddr.getOffset());
				if ((emuError == null) || (emuError.indexOf(pcStr) < 0)) {
					msg.append(", pc=0x");
					msg.append(pcStr);
				}
				failTest(testRunner, msg.toString());
			}
			int ranCnt = pass + fail + callOtherErrors + ignoredFailed + ignoredPassed;
			if ((totalExpectedAsserts != 0) && (totalExpectedAsserts != ranCnt)) {
				failTest(testRunner,
					"ERROR Unexpected number of assertions ( " + passFailText + " )");
			}
			if (fail != 0 || callOtherErrors != 0 || hasFailures) {
				failTest(testRunner,
					"ERROR One or more group tests failed ( " + passFailText + " )");
			}

		}
		catch (Exception e) {
			log(testGroup, "Exception occurred during test", e);
			fail("Exception occurred during test: " + e.getMessage());
		}
		finally {
			if (testRunner != null) {
				logData.unimplementedSet.addAll(testRunner.getUnimplementedPcodeops());
				testRunner.dispose();
			}
			if (program != null) {
				env.release(program);
			}
		}
	}
Single unit test which handles named test group specified during test
instantiation.
Ghidra/Features/Base/src/main/java/ghidra/test/processors/support/ProcessorEmulatorTestAdapter.java