// Run commands
ArgRunnable runRunCommand = new ArgRunnable() {
@Override
public void run(CaptureList args) {
// The second argument "command" may also be missing, if the
// caller used the shortcut.
int startIdx = 1;
if (args.get(1).isEmpty()) {
// Empty array (that is, not even containing an empty string) means that
// we matched and skipped /(?:singleC|c)ommand/
startIdx = 2;
}
String[] flatArgs = new String[args.size() - startIdx];
for (int i = startIdx; i < args.size(); i++) {
flatArgs[i - startIdx] = args.get(i).get(0);
}
try {
mScheduler.addCommand(flatArgs);
} catch (ConfigurationException e) {
printLine("Failed to run command: " + e.toString());
}
}
};
trie.put(runRunCommand, RUN_PATTERN, "c(?:ommand)?", null);
trie.put(runRunCommand, RUN_PATTERN, null);
@Override
public void run() {
List arrrgs = mMainArgs;
if (mScheduler == null) {
throw new IllegalStateException("command scheduler hasn't been set");
}
try {
// Check System.console() since jline doesn't seem to consistently know whether or not
// the console is functional.
if (!isConsoleFunctional()) {
if (arrrgs.isEmpty()) {
printLine("No commands for non-interactive mode; exiting.");
// FIXME: need to run the scheduler here so that the things blocking on it
// FIXME: will be released.
mScheduler.start();
mScheduler.await();
return;
} else {
printLine("Non-interactive mode: Running initial command then exiting.");
mShouldExit = true;
}
}
// Wait for the CommandScheduler to start. It will hold the JVM open (since the Console
// thread is a Daemon thread), and also we require it to have started so that we can
// start processing user input.
mScheduler.start();
mScheduler.await();
String input = "";
CaptureList groups = new CaptureList();
String[] tokens;
// Note: since Console is a daemon thread, the JVM may exit without us actually leaving
// this read loop. This is by design.
do {
if (arrrgs.isEmpty()) {
input = getConsoleInput();
if (input == null) {
// Usually the result of getting EOF on the console
printLine("");
printLine("Received EOF; quitting...");
mShouldExit = true;
break;
}
tokens = null;
try {
Log.d("TokenizeLine input",input);
tokens = QuotationAwareTokenizer.tokenizeLine(input);
for (int i = 0;i stringList = groups.get(i);
for (int j=0;j
while (!isShutdown()) {
// wait until processing is required again
CLog.logAndDisplay(LogLevel.INFO, "Ready to wait 30s");
mCommandProcessWait.waitAndReset(mPollTime);
checkInvocations();
processReadyCommands(manager);
postProcessReadyCommands();
}
protected void processReadyCommands(IDeviceManager manager) {
CLog.d("processReadyCommands...");
Map scheduledCommandMap = new HashMap<>();
// minimize length of synchronized block by just matching commands with device first,
// then scheduling invocations/adding looping commands back to queue
synchronized (this) {
// sort ready commands by priority, so high priority commands are matched first
Collections.sort(mReadyCommands, new ExecutableCommandComparator());
Iterator cmdIter = mReadyCommands.iterator();
while (cmdIter.hasNext()) {
ExecutableCommand cmd = cmdIter.next();
IConfiguration config = cmd.getConfiguration();
IInvocationContext context = new InvocationContext();
context.setConfigurationDescriptor(config.getConfigurationDescription());
Map devices = allocateDevices(config, manager);
Iterator> it = devices.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = it.next();
System.out.println("key= " + entry.getKey());
}
if (!devices.isEmpty()) {
cmdIter.remove();
mExecutingCommands.add(cmd);
context.addAllocatedDevice(devices);
// track command matched with device
scheduledCommandMap.put(cmd, context);
// clean warned list to avoid piling over time.
mUnscheduledWarning.remove(cmd);
} else {
if (!mUnscheduledWarning.contains(cmd)) {
CLog.logAndDisplay(LogLevel.DEBUG, "No available device matching all the "
+ "config's requirements for cmd id %d.",
cmd.getCommandTracker().getId());
// make sure not to record since it may contains password
System.out.println(
String.format(
"The command %s will be rescheduled.",
Arrays.toString(cmd.getCommandTracker().getArgs())));
mUnscheduledWarning.add(cmd);
}
}
}
}
// now actually execute the commands
for (Map.Entry cmdDeviceEntry : scheduledCommandMap
.entrySet()) {
ExecutableCommand cmd = cmdDeviceEntry.getKey();
startInvocation(cmdDeviceEntry.getValue(), cmd,
new FreeDeviceHandler(getDeviceManager()));
if (cmd.isLoopMode()) {
addNewExecCommandToQueue(cmd.getCommandTracker());
}
}
CLog.d("done processReadyCommands...");
}
private void startInvocation(
IInvocationContext context,
ExecutableCommand cmd,
IScheduledInvocationListener... listeners) {
initInvocation();
// Check if device is not used in another invocation.
throwIfDeviceInInvocationThread(context.getDevices());
CLog.d("starting invocation for command id %d", cmd.getCommandTracker().getId());
// Name invocation with first device serial
final String invocationName = String.format("Invocation-%s",
context.getSerials().get(0));
CLog.d("create an invocation thread:%s",invocationName);
InvocationThread invocationThread = new InvocationThread(invocationName, context, cmd,
listeners);
logInvocationStartedEvent(cmd.getCommandTracker(), context);
invocationThread.start();
addInvocationThread(invocationThread);
}
private IConfiguration createConfiguration(String[] args) throws ConfigurationException {
// check if the command should be sandboxed
if (isCommandSandboxed(args)) {
// Create an sandboxed configuration based on the sandbox of the scheduler.
ISandbox sandbox = createSandbox();
return SandboxConfigurationFactory.getInstance()
.createConfigurationFromArgs(args, getKeyStoreClient(), sandbox, new RunUtil());
}
return getConfigFactory().createConfigurationFromArgs(args, null, getKeyStoreClient());
}
@Override
public IConfiguration createConfigurationFromArgs(String[] arrayArgs,
List unconsumedArgs, IKeyStoreClient keyStoreClient)
throws ConfigurationException {
List listArgs = new ArrayList(arrayArgs.length);
// FIXME: Update parsing to not care about arg order.
String[] reorderedArrayArgs = reorderArgs(arrayArgs);
IConfiguration config =
internalCreateConfigurationFromArgs(reorderedArrayArgs, listArgs, keyStoreClient);
config.setCommandLine(arrayArgs);
if (listArgs.contains("--" + CommandOptions.DRY_RUN_OPTION)) {
// In case of dry-run, we replace the KeyStore by a dry-run one.
CLog.w("dry-run detected, we are using a dryrun keystore");
keyStoreClient = new DryRunKeyStore();
}
final List tmpUnconsumedArgs = config.setOptionsFromCommandLineArgs(
listArgs, keyStoreClient);
if (unconsumedArgs == null && tmpUnconsumedArgs.size() > 0) {
// (unconsumedArgs == null) is taken as a signal that the caller
// expects all args to
// be processed.
throw new ConfigurationException(String.format(
"Invalid arguments provided. Unprocessed arguments: %s", tmpUnconsumedArgs));
} else if (unconsumedArgs != null) {
// Return the unprocessed args
unconsumedArgs.addAll(tmpUnconsumedArgs);
}
return config;
}
private IConfiguration internalCreateConfigurationFromArgs(String[] arrayArgs,
List optionArgsRef, IKeyStoreClient keyStoreClient)
throws ConfigurationException {
if (arrayArgs.length == 0) {
throw new ConfigurationException("Configuration to run was not specified");
}
final List listArgs = new ArrayList<>(Arrays.asList(arrayArgs));
// first arg is config name
final String configName = listArgs.remove(0);
Log.d(LOG_TAG,"configName:"+configName);
// Steal ConfigurationXmlParser arguments from the command line
final ConfigurationXmlParserSettings parserSettings = new ConfigurationXmlParserSettings();
final ArgsOptionParser templateArgParser = new ArgsOptionParser(parserSettings);
if (keyStoreClient != null) {
templateArgParser.setKeyStore(keyStoreClient);
}
optionArgsRef.addAll(templateArgParser.parseBestEffort(listArgs));
ConfigurationDef configDef = getConfigurationDef(configName, false,
parserSettings.templateMap);
if (!parserSettings.templateMap.isEmpty()) {
// remove the bad ConfigDef from the cache.
for (ConfigId cid : mConfigDefMap.keySet()) {
if (mConfigDefMap.get(cid) == configDef) {
CLog.d("Cleaning the cache for this configdef");
mConfigDefMap.remove(cid);
break;
}
}
throw new ConfigurationException(String.format("Unused template:map parameters: %s",
parserSettings.templateMap.toString()));
}
return configDef.createConfiguration();
}
@Override
public ConfigurationDef getConfigurationDef(String name, Map templateMap)
throws ConfigurationException {
String configName = name;
if (!isBundledConfig(name)) {
configName = getAbsolutePath(null, name);
// If the config file does not exist in the default location, try to locate it from
// test cases directories defined by environment variables.
File configFile = new File(configName);
if (!configFile.exists()) {
configFile = getTestCaseConfigPath(name);
if (configFile != null) {
configName = configFile.getAbsolutePath();
}
}
}
final ConfigId configId = new ConfigId(name, templateMap);
ConfigurationDef def = mConfigDefMap.get(configId);
if (def == null || def.isStale()) {
def = new ConfigurationDef(configName);
loadConfiguration(configName, def, null, templateMap);
mConfigDefMap.put(configId, def);
} else {
if (templateMap != null) {
// Clearing the map before returning the cached config to
// avoid seeing them as unused.
templateMap.clear();
}
}
return def;
}
void addObject(String objectTypeName, Attributes attributes) throws SAXException {
if (Configuration.DEVICE_NAME.equals(objectTypeName)) {
// We still want to add a standalone device without any inner object.
String deviceName = attributes.getValue("name");
if (!mListDevice.contains(deviceName)) {
mListDevice.add(deviceName);
mConfigDef.addConfigObjectDef(objectTypeName,
DeviceConfigurationHolder.class.getCanonicalName());
mConfigDef.addExpectedDevice(deviceName);
}
} else {
String className = attributes.getValue("class");
if (className == null) {
throwException(String.format("Missing class attribute for object %s",
objectTypeName));
}
if (mCurrentDeviceObject != null) {
// Add the device name as a namespace to the type
objectTypeName = mCurrentDeviceObject + OptionSetter.NAMESPACE_SEPARATOR
+ objectTypeName;
}
int classCount = mConfigDef.addConfigObjectDef(objectTypeName, className);
mCurrentConfigObject = String.format("%s%c%d", className,
OptionSetter.NAMESPACE_SEPARATOR, classCount);
}
}
private IConfiguration internalCreateConfigurationFromArgs(String[] arrayArgs,
List optionArgsRef, IKeyStoreClient keyStoreClient)
throws ConfigurationException {
if (arrayArgs.length == 0) {
throw new ConfigurationException("Configuration to run was not specified");
}
final List listArgs = new ArrayList<>(Arrays.asList(arrayArgs));
// first arg is config name
final String configName = listArgs.remove(0);
Log.d(LOG_TAG,"configName:"+configName);
// Steal ConfigurationXmlParser arguments from the command line
final ConfigurationXmlParserSettings parserSettings = new ConfigurationXmlParserSettings();
final ArgsOptionParser templateArgParser = new ArgsOptionParser(parserSettings);
if (keyStoreClient != null) {
templateArgParser.setKeyStore(keyStoreClient);
}
optionArgsRef.addAll(templateArgParser.parseBestEffort(listArgs));
ConfigurationDef configDef = getConfigurationDef(configName, false,
parserSettings.templateMap);
if (!parserSettings.templateMap.isEmpty()) {
// remove the bad ConfigDef from the cache.
for (ConfigId cid : mConfigDefMap.keySet()) {
if (mConfigDefMap.get(cid) == configDef) {
CLog.d("Cleaning the cache for this configdef");
mConfigDefMap.remove(cid);
break;
}
}
throw new ConfigurationException(String.format("Unused template:map parameters: %s",
parserSettings.templateMap.toString()));
}
return configDef.createConfiguration();
}
IConfiguration createConfiguration() throws ConfigurationException {
IConfiguration config = new Configuration(getName(), getDescription());
List deviceObjectList = new ArrayList();
IDeviceConfiguration defaultDeviceConfig =
new DeviceConfigurationHolder(DEFAULT_DEVICE_NAME);
if (!mMultiDeviceMode) {
// We still populate a default device config to avoid special logic in the rest of the
// harness.
deviceObjectList.add(defaultDeviceConfig);
} else {
for (String name : mExpectedDevices) {
deviceObjectList.add(new DeviceConfigurationHolder(name));
}
}
for (Map.Entry> objClassEntry : mObjectClassMap.entrySet()) {
List objectList = new ArrayList(objClassEntry.getValue().size());
String entryName = objClassEntry.getKey();
boolean shouldAddToFlatConfig = true;
for (ConfigObjectDef configDef : objClassEntry.getValue()) {
Object configObject = createObject(objClassEntry.getKey(), configDef.mClassName);
Matcher matcher = null;
if (mMultiDeviceMode) {
matcher = MULTI_PATTERN.matcher(entryName);
}
if (mMultiDeviceMode && matcher.find()) {
// If we find the device namespace, fetch the matching device or create it if
// it doesn't exists.
IDeviceConfiguration multiDev = null;
shouldAddToFlatConfig = false;
for (IDeviceConfiguration iDevConfig : deviceObjectList) {
if (matcher.group(1).equals(iDevConfig.getDeviceName())) {
multiDev = iDevConfig;
break;
}
}
if (multiDev == null) {
multiDev = new DeviceConfigurationHolder(matcher.group(1));
deviceObjectList.add(multiDev);
}
// We reference the original object to the device and not to the flat list.
multiDev.addSpecificConfig(configObject);
multiDev.addFrequency(configObject, configDef.mAppearanceNum);
} else {
if (Configuration.doesBuiltInObjSupportMultiDevice(entryName)) {
defaultDeviceConfig.addSpecificConfig(configObject);
defaultDeviceConfig.addFrequency(configObject, configDef.mAppearanceNum);
} else {
// Only add to flat list if they are not part of multi device config.
objectList.add(configObject);
}
}
}
if (shouldAddToFlatConfig) {
config.setConfigurationObjectList(entryName, objectList);
}
}
// We always add the device configuration list so we can rely on it everywhere
config.setConfigurationObjectList(Configuration.DEVICE_NAME, deviceObjectList);
config.injectOptionValues(mOptionList);
return config;
}
@Override
public void run() {
Map deviceStates = new HashMap<>();
for (ITestDevice device : mInvocationContext.getDevices()) {
deviceStates.put(device, FreeDeviceState.AVAILABLE);
}
mStartTime = System.currentTimeMillis();
ITestInvocation instance = getInvocation();
IConfiguration config = mCmd.getConfiguration();
// Copy the command options invocation attributes to the invocation.
// TODO: Implement a locking/read-only mechanism to prevent unwanted attributes to be
// added during the invocation.
if (!config.getCommandOptions().getInvocationData().isEmpty()) {
mInvocationContext.addInvocationAttributes(
config.getCommandOptions().getInvocationData());
}
try {
mCmd.commandStarted();
long invocTimeout = config.getCommandOptions().getInvocationTimeout();
if (invocTimeout > 0) {
CLog.i("Setting a timer for the invocation in %sms", invocTimeout);
mExecutionTimer.schedule(mInvocationThreadMonitor, invocTimeout);
}
instance.invoke(mInvocationContext, config,
new Rescheduler(mCmd.getCommandTracker()), mListeners);
}
@VisibleForTesting
void runTests(
IInvocationContext context, IConfiguration config, ITestInvocationListener listener)
throws DeviceNotAvailableException {
// Wrap collectors in each other and collection will be sequential
for (IMetricCollector collector : config.getMetricCollectors()) {
listener = collector.init(context, listener);
}
for (IRemoteTest test : config.getTests()) {
// For compatibility of those receivers, they are assumed to be single device alloc.
if (test instanceof IDeviceTest) {
((IDeviceTest)test).setDevice(context.getDevices().get(0));
}
if (test instanceof IBuildReceiver) {
((IBuildReceiver)test).setBuild(context.getBuildInfo(
context.getDevices().get(0)));
}
if (test instanceof ISystemStatusCheckerReceiver) {
((ISystemStatusCheckerReceiver) test).setSystemStatusChecker(
config.getSystemStatusCheckers());
}
// TODO: consider adding receivers for only the list of ITestDevice and IBuildInfo.
if (test instanceof IMultiDeviceTest) {
((IMultiDeviceTest)test).setDeviceInfos(context.getDeviceBuildMap());
}
if (test instanceof IInvocationContextReceiver) {
((IInvocationContextReceiver)test).setInvocationContext(context);
}
test.run(listener);
}
}
@Override
public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
...
LinkedList modules = initializeModuleRepo();
...
while (!modules.isEmpty()) {
// Make sure we remove the modules from the reference list when we are done with
// them.
IModuleDef module = modules.poll();
long start = System.currentTimeMillis();
if (mRebootPerModule) {
if ("user".equals(mDevice.getProperty("ro.build.type"))) {
CLog.e("reboot-per-module should only be used during development, "
+ "this is a\" user\" build device");
} else {
CLog.logAndDisplay(LogLevel.INFO, "Rebooting device before starting next "
+ "module");
mDevice.reboot();
}
}
try {
module.run(listener);
} catch (DeviceUnresponsiveException due) {
...
}
protected LinkedList initializeModuleRepo()
throws DeviceNotAvailableException, FileNotFoundException {
// FIXME: Each shard will do a full initialization which is not optimal. Need a way
// to be more specific on what to initialize.
synchronized (mModuleRepo) {
if (!mModuleRepo.isInitialized()) {
setupFilters();
// Initialize the repository, {@link CompatibilityBuildHelper#getTestsDir} can
// throw a {@link FileNotFoundException}
mModuleRepo.initialize(mTotalShards, mShardIndex, mBuildHelper.getTestsDir(),
getAbis(), mDeviceTokens, mTestArgs, mModuleArgs, mIncludeFilters,
mExcludeFilters, mModuleMetadataIncludeFilter, mModuleMetadataExcludeFilter,
mBuildHelper.getBuildInfo());
// Add the entire list of modules to the CompatibilityBuildHelper for reporting
mBuildHelper.setModuleIds(mModuleRepo.getModuleIds());
int count = UniqueModuleCountUtil.countUniqueModules(mModuleRepo.getTokenModules())
+ UniqueModuleCountUtil.countUniqueModules(
mModuleRepo.getNonTokenModules());
CLog.logAndDisplay(LogLevel.INFO, "========================================");
CLog.logAndDisplay(LogLevel.INFO, "Starting a run with %s unique modules.", count);
CLog.logAndDisplay(LogLevel.INFO, "========================================");
} else {
CLog.d("ModuleRepo already initialized.");
}
// Get the tests to run in this shard
return mModuleRepo.getModules(getDevice().getSerialNumber(), mShardIndex);
}
}
@Override
public void initialize(int totalShards, Integer shardIndex, File testsDir, Set abis,
List deviceTokens, List testArgs, List moduleArgs,
Set includeFilters, Set excludeFilters,
MultiMap metadataIncludeFilters,
MultiMap metadataExcludeFilters,
IBuildInfo buildInfo) {
CLog.d("Initializing ModuleRepo\nShards:%d\nTests Dir:%s\nABIs:%s\nDevice Tokens:%s\n" +
"Test Args:%s\nModule Args:%s\nIncludes:%s\nExcludes:%s",
totalShards, testsDir.getAbsolutePath(), abis, deviceTokens, testArgs, moduleArgs,
includeFilters, excludeFilters);
mInitialized = true;
mTotalShards = totalShards;
mShardIndex = shardIndex;
synchronized (lock) {
if (mTokenModuleScheduled == null) {
mTokenModuleScheduled = new HashSet<>();
}
}
for (String line : deviceTokens) {
String[] parts = line.split(":");
if (parts.length == 2) {
String key = parts[0];
String value = parts[1];
Set list = mDeviceTokens.get(key);
if (list == null) {
list = new HashSet<>();
mDeviceTokens.put(key, list);
}
list.add(value);
} else {
throw new IllegalArgumentException(
String.format("Could not parse device token: %s", line));
}
}
putArgs(testArgs, mTestArgs);
putArgs(moduleArgs, mModuleArgs);
mIncludeAll = includeFilters.isEmpty();
// Include all the inclusions
addFilters(includeFilters, mIncludeFilters, abis);
// Exclude all the exclusions
addFilters(excludeFilters, mExcludeFilters, abis);
File[] configFiles = testsDir.listFiles(new ConfigFilter());
if (configFiles.length == 0) {
throw new IllegalArgumentException(
String.format("No config files found in %s", testsDir.getAbsolutePath()));
}
Map shardedTestCounts = new HashMap<>();
for (File configFile : configFiles) {
final String name = configFile.getName().replace(CONFIG_EXT, "");
final String[] pathArg = new String[] { configFile.getAbsolutePath() };
try {
// Invokes parser to process the test module config file
// Need to generate a different config for each ABI as we cannot guarantee the
// configs are idempotent. This however means we parse the same file multiple times
for (IAbi abi : abis) {
String id = AbiUtils.createId(abi.getName(), name);
if (!shouldRunModule(id)) {
// If the module should not run tests based on the state of filters,
// skip this name/abi combination.
continue;
}
IConfiguration config = mConfigFactory.createConfigurationFromArgs(pathArg);
if (!filterByConfigMetadata(config,
metadataIncludeFilters, metadataExcludeFilters)) {
// if the module config did not pass the metadata filters, it's excluded
// from execution
continue;
}
Map> args = new HashMap<>();
if (mModuleArgs.containsKey(name)) {
args.putAll(mModuleArgs.get(name));
}
if (mModuleArgs.containsKey(id)) {
args.putAll(mModuleArgs.get(id));
}
injectOptionsToConfig(args, config);
List tests = config.getTests();
for (IRemoteTest test : tests) {
prepareTestClass(name, abi, config, test);
}
List shardedTests = tests;
if (mTotalShards > 1) {
shardedTests = splitShardableTests(tests, buildInfo);
}
if (shardedTests.size() > 1) {
shardedTestCounts.put(id, shardedTests.size());
}
for (IRemoteTest test : shardedTests) {
addModuleDef(name, abi, test, pathArg);
}
}
} catch (ConfigurationException e) {
throw new RuntimeException(String.format("error parsing config file: %s",
configFile.getName()), e);
}
}
mExcludeFilters.clear();
TestRunHandler.setTestRuns(new CompatibilityBuildHelper(buildInfo), shardedTestCounts);
}
@Override
public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
CLog.d("Running module %s", toString());
runPreparerSetups();
CLog.d("Test: %s", mTest.getClass().getSimpleName());
prepareTestClass();
IModuleListener moduleListener = new ModuleListener(this, listener);
// Guarantee events testRunStarted and testRunEnded in case underlying test runner does not
ModuleFinisher moduleFinisher = new ModuleFinisher(moduleListener);
mTest.run(moduleFinisher);
moduleFinisher.finish();
// Tear down
runPreparerTeardowns();
}
@SuppressWarnings("deprecation")
@Override
public void run(ITestInvocationListener listener)
throws IllegalArgumentException, DeviceNotAvailableException {
if (mDevice == null) {
throw new DeviceNotAvailableException("Device has not been set");
}
if (mTestCasePath == null) {
if (!mBinaryTestSource.isEmpty()) {
String template;
switch (mBinaryTestType) {
case BINARY_TEST_TYPE_GTEST:
template = TEMPLATE_GTEST_BINARY_TEST_PATH;
break;
case BINARY_TEST_TYPE_HAL_HIDL_GTEST:
template = TEMPLATE_HAL_HIDL_GTEST_PATH;
break;
case BINARY_TEST_TYPE_HOST_BINARY_TEST:
template = TEMPLATE_HOST_BINARY_TEST_PATH;
break;
default:
template = TEMPLATE_BINARY_TEST_PATH;
}
CLog.i("Using default test case template at %s.", template);
setTestCasePath(template);
if (mEnableCoverage && !mGlobalCoverage) {
CLog.e("Only global coverage is supported for test type %s.", mBinaryTestType);
throw new RuntimeException("Failed to produce VTS runner test config");
}
} else if (mBinaryTestType.equals(BINARY_TEST_TYPE_HAL_HIDL_REPLAY_TEST)) {
setTestCasePath(TEMPLATE_HAL_HIDL_REPLAY_TEST_PATH);
} else if (mBinaryTestType.equals(BINARY_TEST_TYPE_LLVMFUZZER)) {
// Fuzz test don't need test-case-path.
setTestCasePath(TEMPLATE_LLVMFUZZER_TEST_PATH);
} else {
throw new IllegalArgumentException("test-case-path is not set.");
}
}
setPythonPath();
if (mPythonBin == null) {
mPythonBin = getPythonBinary();
}
if (mRunUtil == null){
mRunUtil = new RunUtil();
mRunUtil.setEnvVariable(PYTHONPATH, mPythonPath);
}
doRunTest(listener);
}
首先看看 WeakReference
wiki 上 Weak reference 的一个例子:
public class ReferenceTest {
public static void main(String[] args) throws InterruptedException {
WeakReference r = new Wea
有一个线上环境使用的是c3p0数据库,为外部提供接口服务。最近访问压力增大后台tomcat的日志里面频繁出现
com.mchange.v2.resourcepool.TimeoutException: A client timed out while waiting to acquire a resource from com.mchange.v2.resourcepool.BasicResou
https://weblogs.java.net/blog/mriem/archive/2013/11/22/jsf-tip-45-create-composite-component-custom-namespace
When you developed a composite component the namespace you would be seeing would
一、复本集为什么要加入Arbiter这个角色 回答这个问题,要从复本集的存活条件和Aribter服务器的特性两方面来说。 什么是Artiber? An arbiter does
not have a copy of data set and
cannot become a primary. Replica sets may have arbiters to add a
# include <stdio.h>
int main(void)
{
int a[5] = {1, 2, 3, 4, 5};
//a 是数组的名字 5是表示数组元素的个数,并且这五个元素分别用a[0], a[1]...a[4]
int i;
for (i=0; i<5; ++i)
printf("%d\n",