/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
#include <sys/types.h>
#include <regex.h>
#include <stdlib.h>
#include <stdio.h>
#include "Log.h"
#include "audio/RemoteAudio.h"
#include "ClientImpl.h"
#include "Report.h"
#include "Settings.h"
#include "StringUtil.h"
#include "task/TaskCase.h"
static const android::String8 STR_NAME("name");
static const android::String8 STR_VERSION("version");
static const android::String8 STR_DESCRIPTION("description");
TaskCase::TaskCase()
: TaskGeneric(TaskGeneric::ETaskCase),
mClient(NULL)
{
const android::String8* list[] = {&STR_NAME, &STR_VERSION, &STR_DESCRIPTION, NULL};
registerSupportedStringAttributes(list);
}
TaskCase::~TaskCase()
{
delete mClient;
}
bool TaskCase::getCaseName(android::String8& name) const
{
if (!findStringAttribute(STR_NAME, name)) {
LOGW("TaskCase no name");
return false;
}
return true;
}
bool TaskCase::addChild(TaskGeneric* child)
{
if ((child->getType() != TaskGeneric::ETaskSetup)
&& (child->getType() != TaskGeneric::ETaskAction)
&& (child->getType() != TaskGeneric::ETaskSave)) {
LOGE("TestCase::addChild wrong child type %d", child->getType());
return false;
}
return TaskGeneric::addChild(child);
}
template <typename T> bool registerGeneric(
typename std::map<android::String8, T>& map,
const android::String8& name, T& data)
{
typename std::map<android::String8, T>::iterator it;
it = map.find(name);
if (it != map.end()) {
LOGV("registerGeneric key %s already registered", name.string());
return false;
}
LOGD("registerGeneric registered key %s", name.string());
map[name] = data;
return true;
}
template <typename T> bool findGeneric(typename std::map<android::String8, T>& map,
const android::String8& name, T& data)
{
LOGD("findGeneric key %s", name.string());
typename std::map<android::String8, T>::iterator it;
it = map.find(name);
if (it == map.end()) {
return false;
}
data = it->second;
return true;
}
template <typename T> bool updateGeneric(typename std::map<android::String8, T>& map,
const android::String8& name, T& data)
{
LOGD("updateGeneric key %s", name.string());
typename std::map<android::String8, T>::iterator it;
it = map.find(name);
if (it == map.end()) {
return false;
}
it->second = data;
return true;
}
// return all the matches for the given regular expression.
// name string and the data itself is copied.
template <typename T> typename std::list<std::pair<android::String8, T> >* findAllGeneric(
typename std::map<android::String8, T>& map, const char* re)
{
regex_t regex;
if (regcomp(®ex, re, REG_EXTENDED | REG_NOSUB) != 0) {
LOGE("regcomp failed");
return NULL;
}
typename std::map<android::String8, T>::iterator it;
typename std::list<std::pair<android::String8, T> >* list = NULL;
for (it = map.begin(); it != map.end(); it++) {
if (regexec(®ex, it->first, 0, NULL, 0) == 0) {
if (list == NULL) { // create only when found
list = new std::list<std::pair<android::String8, T> >();
if (list == NULL) {
regfree(®ex);
return NULL;
}
}
typename std::pair<android::String8, T> match(it->first, it->second);
list->push_back(match);
}
}
regfree(®ex);
return list;
}
bool TaskCase::registerBuffer(const android::String8& orig, android::sp<Buffer>& buffer)
{
android::String8 translated;
if (!translateVarName(orig, translated)) {
return false;
}
return registerGeneric<android::sp<Buffer> >(mBufferList, translated, buffer);
}
bool TaskCase::updateBuffer(const android::String8& orig, android::sp<Buffer>& buffer)
{
android::String8 translated;
if (!translateVarName(orig, translated)) {
return false;
}
return updateGeneric<android::sp<Buffer> >(mBufferList, translated, buffer);
}
android::sp<Buffer> TaskCase::findBuffer(const android::String8& orig)
{
android::String8 translated;
android::sp<Buffer> result;
if (!translateVarName(orig, translated)) {
return result;
}
findGeneric<android::sp<Buffer> >(mBufferList, translated, result);
return result;
}
std::list<TaskCase::BufferPair>* TaskCase::findAllBuffers(const android::String8& re)
{
android::String8 translated;
if (!translateVarName(re, translated)) {
return NULL;
}
return findAllGeneric<android::sp<Buffer> >(mBufferList, translated.string());
}
bool TaskCase::registerValue(const android::String8& orig, Value& val)
{
android::String8 translated;
if (!translateVarName(orig, translated)) {
return false;
}
LOGD("str %x", translated.string());
return registerGeneric<Value>(mValueList, translated, val);
}
bool TaskCase::updateValue(const android::String8& orig, Value& val)
{
android::String8 translated;
if (!translateVarName(orig, translated)) {
return false;
}
return updateGeneric<Value>(mValueList, translated, val);
}
bool TaskCase::findValue(const android::String8& orig, Value& val)
{
android::String8 translated;
if (!translateVarName(orig, translated)) {
return false;
}
return findGeneric<Value>(mValueList, translated, val);
}
std::list<TaskCase::ValuePair>* TaskCase::findAllValues(const android::String8& re)
{
android::String8 translated;
if (!translateVarName(re, translated)) {
return NULL;
}
return findAllGeneric<Value>(mValueList, translated.string());
}
bool TaskCase::registerIndex(const android::String8& name, int value)
{
return registerGeneric<int>(mIndexList, name, value);
}
bool TaskCase::updateIndex(const android::String8& name, int value)
{
return updateGeneric<int>(mIndexList, name, value);
}
bool TaskCase::findIndex(const android::String8& name, int& val)
{
return findGeneric<int>(mIndexList, name, val);
}
std::list<TaskCase::IndexPair>* TaskCase::findAllIndices(const android::String8& re)
{
android::String8 translated;
if (!translateVarName(re, translated)) {
return NULL;
}
return findAllGeneric<int>(mIndexList, translated.string());
}
bool TaskCase::translateVarName(const android::String8& orig, android::String8& translated)
{
const char* src = orig.string();
const int nmatch = 2;
regmatch_t pmatch[nmatch];
regex_t re;
size_t strStart = 0;
if (regcomp(&re, "[a-z0-9_]*[$]([a-z0-9]+)[_]*", REG_EXTENDED) != 0) {
LOGE("regcomp failed");
return false;
}
bool result = false;
size_t matchStart = 0;
size_t matchEnd = 0;
while (regexec(&re, src, nmatch, pmatch, 0) == 0) {
matchStart = strStart + pmatch[1].rm_so;
matchEnd = strStart + pmatch[1].rm_eo;
translated.append(StringUtil::substr(orig, strStart, pmatch[1].rm_so - 1)); //-1 for $
android::String8 indexName;
indexName.append(StringUtil::substr(orig, matchStart, matchEnd - matchStart));
int val;
if (!findIndex(indexName, val)) {
LOGE("TaskCase::translateVarName no index with name %s", indexName.string());
regfree(&re);
return false;
}
translated.appendFormat("%d", val);
LOGD("match found strStart %d, matchStart %d, matchEnd %d, converted str %s",
strStart, matchStart, matchEnd, translated.string());
src += pmatch[1].rm_eo;
strStart += pmatch[1].rm_eo;
}
if (matchEnd < orig.length()) {
//LOGD("%d %d", matchEnd, orig.length());
translated.append(StringUtil::substr(orig, matchEnd, orig.length() - matchEnd));
}
LOGD("translated str %s to %s", orig.string(), translated.string());
regfree(&re);
return true;
}
android::sp<RemoteAudio>& TaskCase::getRemoteAudio()
{
if (mClient == NULL) {
mClient = new ClientImpl();
ASSERT(mClient->init(Settings::Instance()->getSetting(Settings::EADB)));
}
return mClient->getAudio();
}
void TaskCase::releaseRemoteAudio()
{
delete mClient;
mClient = NULL;
}
void TaskCase::setDetails(android::String8 details)
{
mDetails = details;
}
const android::String8& TaskCase::getDetails() const
{
return mDetails;
}
TaskGeneric::ExecutionResult TaskCase::run()
{
android::String8 name;
android::String8 version;
//LOGI("str %d, %d", strlen(STR_NAME), strlen(STR_VERSION));
if (!findStringAttribute(STR_NAME, name) || !findStringAttribute(STR_VERSION, version)) {
LOGW("TaskCase::run no name or version information");
}
MSG("== Test case %s version %s started ==", name.string(), version.string());
std::list<TaskGeneric*>::iterator i = getChildren().begin();
std::list<TaskGeneric*>::iterator end = getChildren().end();
TaskGeneric* setup = *i;
i++;
TaskGeneric* action = *i;
i++;
TaskGeneric* save = (i == end)? NULL : *i;
if (save == NULL) {
LOGW("No save stage in test case");
}
bool testPassed = true;
TaskGeneric::ExecutionResult result = setup->run();
TaskGeneric::ExecutionResult resultAction(TaskGeneric::EResultOK);
if (result != TaskGeneric::EResultOK) {
MSG("== setup stage failed %d ==", result);
testPassed = false;
} else {
resultAction = action->run();
if (resultAction != TaskGeneric::EResultPass) {
MSG("== action stage failed %d ==", resultAction);
testPassed = false;
}
// save done even for failure if possible
if (save != NULL) {
result = save->run();
}
if (result != TaskGeneric::EResultOK) {
MSG("== save stage failed %d ==", result);
testPassed = false;
}
}
if (testPassed) {
result = TaskGeneric::EResultPass;
MSG("== Case %s Passed ==", name.string());
Report::Instance()->addCasePassed(this);
} else {
if (resultAction != TaskGeneric::EResultOK) {
result = resultAction;
}
MSG("== Case %s Failed ==", name.string());
Report::Instance()->addCaseFailed(this);
}
// release remote audio for other cases to use
releaseRemoteAudio();
return result;
}