forked from qt-creator/qt-creator
		
	Amends d886285ff0.
Fixes: QTCREATORBUG-22028
Change-Id: Ief97520a7db9bc90ed43a0b463008fc57ced0cea
Reviewed-by: David Schulz <david.schulz@qt.io>
		
	
		
			
				
	
	
		
			478 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			478 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/****************************************************************************
 | 
						|
**
 | 
						|
** Copyright (C) 2016 The Qt Company Ltd.
 | 
						|
** Contact: https://www.qt.io/licensing/
 | 
						|
**
 | 
						|
** This file is part of Qt Creator.
 | 
						|
**
 | 
						|
** Commercial License Usage
 | 
						|
** Licensees holding valid commercial Qt licenses may use this file in
 | 
						|
** accordance with the commercial license agreement provided with the
 | 
						|
** Software or, alternatively, in accordance with the terms contained in
 | 
						|
** a written agreement between you and The Qt Company. For licensing terms
 | 
						|
** and conditions see https://www.qt.io/terms-conditions. For further
 | 
						|
** information use the contact form at https://www.qt.io/contact-us.
 | 
						|
**
 | 
						|
** GNU General Public License Usage
 | 
						|
** Alternatively, this file may be used under the terms of the GNU
 | 
						|
** General Public License version 3 as published by the Free Software
 | 
						|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
 | 
						|
** included in the packaging of this file. Please review the following
 | 
						|
** information to ensure the GNU General Public License requirements will
 | 
						|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
 | 
						|
**
 | 
						|
****************************************************************************/
 | 
						|
 | 
						|
#include "autotesticons.h"
 | 
						|
#include "autotestplugin.h"
 | 
						|
#include "testresultdelegate.h"
 | 
						|
#include "testresultmodel.h"
 | 
						|
#include "testsettings.h"
 | 
						|
 | 
						|
#include <projectexplorer/projectexplorericons.h>
 | 
						|
#include <utils/qtcassert.h>
 | 
						|
 | 
						|
#include <QFontMetrics>
 | 
						|
#include <QIcon>
 | 
						|
 | 
						|
namespace Autotest {
 | 
						|
namespace Internal {
 | 
						|
 | 
						|
/********************************* TestResultItem ******************************************/
 | 
						|
 | 
						|
TestResultItem::TestResultItem(const TestResultPtr &testResult)
 | 
						|
    : m_testResult(testResult)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
static QIcon testResultIcon(Result::Type result) {
 | 
						|
    const static QIcon icons[] = {
 | 
						|
        Icons::RESULT_PASS.icon(),
 | 
						|
        Icons::RESULT_FAIL.icon(),
 | 
						|
        Icons::RESULT_XFAIL.icon(),
 | 
						|
        Icons::RESULT_XPASS.icon(),
 | 
						|
        Icons::RESULT_SKIP.icon(),
 | 
						|
        Icons::RESULT_BLACKLISTEDPASS.icon(),
 | 
						|
        Icons::RESULT_BLACKLISTEDFAIL.icon(),
 | 
						|
        Icons::RESULT_BLACKLISTEDXPASS.icon(),
 | 
						|
        Icons::RESULT_BLACKLISTEDXFAIL.icon(),
 | 
						|
        Icons::RESULT_BENCHMARK.icon(),
 | 
						|
        Icons::RESULT_MESSAGEDEBUG.icon(),
 | 
						|
        Icons::RESULT_MESSAGEDEBUG.icon(), // Info gets the same handling as Debug for now
 | 
						|
        Icons::RESULT_MESSAGEWARN.icon(),
 | 
						|
        Icons::RESULT_MESSAGEFATAL.icon(),
 | 
						|
        Icons::RESULT_MESSAGEFATAL.icon(), // System gets same handling as Fatal for now
 | 
						|
        QIcon(),
 | 
						|
        Icons::RESULT_MESSAGEPASSWARN.icon(),
 | 
						|
        Icons::RESULT_MESSAGEFAILWARN.icon(),
 | 
						|
        ProjectExplorer::Icons::DESKTOP_DEVICE.icon(),  // for now
 | 
						|
    }; // provide an icon for unknown??
 | 
						|
 | 
						|
    if (result < 0 || result >= Result::MessageInternal) {
 | 
						|
        switch (result) {
 | 
						|
        case Result::MessageTestCaseSuccess:
 | 
						|
            return icons[Result::Pass];
 | 
						|
        case Result::MessageTestCaseFail:
 | 
						|
            return icons[Result::Fail];
 | 
						|
        case Result::MessageTestCaseSuccessWarn:
 | 
						|
            return icons[16];
 | 
						|
        case Result::MessageTestCaseFailWarn:
 | 
						|
            return icons[17];
 | 
						|
        case Result::Application:
 | 
						|
            return icons[18];
 | 
						|
        default:
 | 
						|
            return QIcon();
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return icons[result];
 | 
						|
}
 | 
						|
 | 
						|
QVariant TestResultItem::data(int column, int role) const
 | 
						|
{
 | 
						|
    switch (role) {
 | 
						|
    case Qt::DecorationRole: {
 | 
						|
        if (!m_testResult)
 | 
						|
            return QVariant();
 | 
						|
        const Result::Type result = m_testResult->result();
 | 
						|
        if (result == Result::MessageLocation && parent())
 | 
						|
            return parent()->data(column, role);
 | 
						|
        return testResultIcon(result);
 | 
						|
    }
 | 
						|
    case Qt::DisplayRole:
 | 
						|
        return m_testResult ? m_testResult->outputString(true) : QVariant();
 | 
						|
    default:
 | 
						|
        return Utils::TreeItem::data(column, role);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void TestResultItem::updateDescription(const QString &description)
 | 
						|
{
 | 
						|
    QTC_ASSERT(m_testResult, return);
 | 
						|
 | 
						|
    m_testResult->setDescription(description);
 | 
						|
}
 | 
						|
 | 
						|
void TestResultItem::updateResult(bool &changed, Result::Type addedChildType)
 | 
						|
{
 | 
						|
    changed = false;
 | 
						|
    const Result::Type old = m_testResult->result();
 | 
						|
    if (old == Result::MessageTestCaseFailWarn) // can't become worse
 | 
						|
        return;
 | 
						|
    if (!TestResult::isMessageCaseStart(old))
 | 
						|
        return;
 | 
						|
 | 
						|
    Result::Type newResult = old;
 | 
						|
    switch (addedChildType) {
 | 
						|
    case Result::Fail:
 | 
						|
    case Result::MessageFatal:
 | 
						|
    case Result::UnexpectedPass:
 | 
						|
    case Result::MessageTestCaseFail:
 | 
						|
        newResult = (old == Result::MessageTestCaseSuccessWarn) ? Result::MessageTestCaseFailWarn
 | 
						|
                                                                : Result::MessageTestCaseFail;
 | 
						|
        break;
 | 
						|
    case Result::MessageTestCaseFailWarn:
 | 
						|
        newResult = Result::MessageTestCaseFailWarn;
 | 
						|
        break;
 | 
						|
    case Result::ExpectedFail:
 | 
						|
    case Result::MessageWarn:
 | 
						|
    case Result::MessageSystem:
 | 
						|
    case Result::Skip:
 | 
						|
    case Result::BlacklistedFail:
 | 
						|
    case Result::BlacklistedPass:
 | 
						|
    case Result::BlacklistedXFail:
 | 
						|
    case Result::BlacklistedXPass:
 | 
						|
    case Result::MessageTestCaseSuccessWarn:
 | 
						|
        newResult = (old == Result::MessageTestCaseFail) ? Result::MessageTestCaseFailWarn
 | 
						|
                                                         : Result::MessageTestCaseSuccessWarn;
 | 
						|
        break;
 | 
						|
    case Result::Pass:
 | 
						|
    case Result::MessageTestCaseSuccess:
 | 
						|
        newResult = (old == Result::MessageIntermediate || old == Result::MessageTestCaseStart)
 | 
						|
                ? Result::MessageTestCaseSuccess : old;
 | 
						|
        break;
 | 
						|
    default:
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    changed = old != newResult;
 | 
						|
    if (changed)
 | 
						|
        m_testResult->setResult(newResult);
 | 
						|
}
 | 
						|
 | 
						|
TestResultItem *TestResultItem::intermediateFor(const TestResultItem *item) const
 | 
						|
{
 | 
						|
    QTC_ASSERT(item, return nullptr);
 | 
						|
    const TestResult *otherResult = item->testResult();
 | 
						|
    for (int row = childCount() - 1; row >= 0; --row) {
 | 
						|
        TestResultItem *child = childAt(row);
 | 
						|
        const TestResult *testResult = child->testResult();
 | 
						|
        if (testResult->result() != Result::MessageIntermediate)
 | 
						|
            continue;
 | 
						|
        if (testResult->isIntermediateFor(otherResult))
 | 
						|
            return child;
 | 
						|
    }
 | 
						|
    return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
TestResultItem *TestResultItem::createAndAddIntermediateFor(const TestResultItem *child)
 | 
						|
{
 | 
						|
    TestResultPtr result(m_testResult->createIntermediateResultFor(child->testResult()));
 | 
						|
    QTC_ASSERT(!result.isNull(), return nullptr);
 | 
						|
    result->setResult(Result::MessageIntermediate);
 | 
						|
    TestResultItem *intermediate = new TestResultItem(result);
 | 
						|
    appendChild(intermediate);
 | 
						|
    return intermediate;
 | 
						|
}
 | 
						|
 | 
						|
/********************************* TestResultModel *****************************************/
 | 
						|
 | 
						|
TestResultModel::TestResultModel(QObject *parent)
 | 
						|
    : Utils::TreeModel<TestResultItem>(new TestResultItem(TestResultPtr()), parent)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
void TestResultModel::updateParent(const TestResultItem *item)
 | 
						|
{
 | 
						|
    QTC_ASSERT(item, return);
 | 
						|
    QTC_ASSERT(item->testResult(), return);
 | 
						|
    TestResultItem *parentItem = item->parent();
 | 
						|
    if (parentItem == rootItem()) // do not update invisible root item
 | 
						|
        return;
 | 
						|
    bool changed = false;
 | 
						|
    parentItem->updateResult(changed, item->testResult()->result());
 | 
						|
    if (!changed)
 | 
						|
        return;
 | 
						|
    emit dataChanged(parentItem->index(), parentItem->index());
 | 
						|
    updateParent(parentItem);
 | 
						|
}
 | 
						|
 | 
						|
void TestResultModel::addTestResult(const TestResultPtr &testResult, bool autoExpand)
 | 
						|
{
 | 
						|
    const int lastRow = rootItem()->childCount() - 1;
 | 
						|
    if (testResult->result() == Result::MessageCurrentTest) {
 | 
						|
        // MessageCurrentTest should always be the last top level item
 | 
						|
        if (lastRow >= 0) {
 | 
						|
            TestResultItem *current = rootItem()->childAt(lastRow);
 | 
						|
            const TestResult *result = current->testResult();
 | 
						|
            if (result && result->result() == Result::MessageCurrentTest) {
 | 
						|
                current->updateDescription(testResult->description());
 | 
						|
                emit dataChanged(current->index(), current->index());
 | 
						|
                return;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        rootItem()->appendChild(new TestResultItem(testResult));
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (testResult->result() == Result::MessageDisabledTests)
 | 
						|
        m_disabled += testResult->line();
 | 
						|
    m_testResultCount[testResult->result()]++;
 | 
						|
 | 
						|
    TestResultItem *newItem = new TestResultItem(testResult);
 | 
						|
 | 
						|
    TestResultItem *root = nullptr;
 | 
						|
    if (AutotestPlugin::settings()->displayApplication) {
 | 
						|
        const QString application = testResult->id();
 | 
						|
        if (!application.isEmpty()) {
 | 
						|
            root = rootItem()->findFirstLevelChild([&application](TestResultItem *child) {
 | 
						|
                QTC_ASSERT(child, return false);
 | 
						|
                return child->testResult()->id() == application;
 | 
						|
            });
 | 
						|
 | 
						|
            if (!root) {
 | 
						|
                TestResult *tmpAppResult = new TestResult(application, application);
 | 
						|
                tmpAppResult->setResult(Result::Application);
 | 
						|
                root = new TestResultItem(TestResultPtr(tmpAppResult));
 | 
						|
                if (lastRow >= 0)
 | 
						|
                    rootItem()->insertChild(lastRow, root);
 | 
						|
                else
 | 
						|
                    rootItem()->appendChild(root);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    TestResultItem *parentItem = findParentItemFor(newItem, root);
 | 
						|
    addFileName(testResult->fileName()); // ensure we calculate the results pane correctly
 | 
						|
    if (parentItem) {
 | 
						|
        parentItem->appendChild(newItem);
 | 
						|
        if (autoExpand)
 | 
						|
            parentItem->expand();
 | 
						|
        updateParent(newItem);
 | 
						|
    } else {
 | 
						|
        if (lastRow >= 0) {
 | 
						|
            TestResultItem *current = rootItem()->childAt(lastRow);
 | 
						|
            const TestResult *result = current->testResult();
 | 
						|
            if (result && result->result() == Result::MessageCurrentTest) {
 | 
						|
                rootItem()->insertChild(current->index().row(), newItem);
 | 
						|
                return;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // there is no MessageCurrentTest at the last row, but we have a toplevel item - just add it
 | 
						|
        rootItem()->appendChild(newItem);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void TestResultModel::removeCurrentTestMessage()
 | 
						|
{
 | 
						|
    TestResultItem *currentMessageItem = rootItem()->findFirstLevelChild([](TestResultItem *it) {
 | 
						|
            return (it->testResult()->result() == Result::MessageCurrentTest);
 | 
						|
    });
 | 
						|
    if (currentMessageItem)
 | 
						|
        destroyItem(currentMessageItem);
 | 
						|
}
 | 
						|
 | 
						|
void TestResultModel::clearTestResults()
 | 
						|
{
 | 
						|
    clear();
 | 
						|
    m_testResultCount.clear();
 | 
						|
    m_disabled = 0;
 | 
						|
    m_fileNames.clear();
 | 
						|
    m_maxWidthOfFileName = 0;
 | 
						|
    m_widthOfLineNumber = 0;
 | 
						|
}
 | 
						|
 | 
						|
const TestResult *TestResultModel::testResult(const QModelIndex &idx)
 | 
						|
{
 | 
						|
    if (idx.isValid())
 | 
						|
        return itemForIndex(idx)->testResult();
 | 
						|
 | 
						|
    return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
void TestResultModel::recalculateMaxWidthOfFileName(const QFont &font)
 | 
						|
{
 | 
						|
    const QFontMetrics fm(font);
 | 
						|
    m_maxWidthOfFileName = 0;
 | 
						|
    for (const QString &fileName : m_fileNames) {
 | 
						|
        int pos = fileName.lastIndexOf('/');
 | 
						|
        m_maxWidthOfFileName = qMax(m_maxWidthOfFileName, fm.width(fileName.mid(pos + 1)));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void TestResultModel::addFileName(const QString &fileName)
 | 
						|
{
 | 
						|
    const QFontMetrics fm(m_measurementFont);
 | 
						|
    int pos = fileName.lastIndexOf('/');
 | 
						|
    m_maxWidthOfFileName = qMax(m_maxWidthOfFileName, fm.width(fileName.mid(pos + 1)));
 | 
						|
    m_fileNames.insert(fileName);
 | 
						|
}
 | 
						|
 | 
						|
int TestResultModel::maxWidthOfFileName(const QFont &font)
 | 
						|
{
 | 
						|
    if (font != m_measurementFont)
 | 
						|
        recalculateMaxWidthOfFileName(font);
 | 
						|
    return m_maxWidthOfFileName;
 | 
						|
}
 | 
						|
 | 
						|
int TestResultModel::maxWidthOfLineNumber(const QFont &font)
 | 
						|
{
 | 
						|
    if (m_widthOfLineNumber == 0 || font != m_measurementFont) {
 | 
						|
        QFontMetrics fm(font);
 | 
						|
        m_measurementFont = font;
 | 
						|
        m_widthOfLineNumber = fm.width("88888");
 | 
						|
    }
 | 
						|
    return m_widthOfLineNumber;
 | 
						|
}
 | 
						|
 | 
						|
TestResultItem *TestResultModel::findParentItemFor(const TestResultItem *item,
 | 
						|
                                                   const TestResultItem *startItem) const
 | 
						|
{
 | 
						|
    QTC_ASSERT(item, return nullptr);
 | 
						|
    TestResultItem *root = startItem ? const_cast<TestResultItem *>(startItem) : nullptr;
 | 
						|
    const TestResult *result = item->testResult();
 | 
						|
    const QString &name = result->name();
 | 
						|
    const QString &id = result->id();
 | 
						|
 | 
						|
    if (root == nullptr && !name.isEmpty()) {
 | 
						|
        for (int row = rootItem()->childCount() - 1; row >= 0; --row) {
 | 
						|
            TestResultItem *tmp = rootItem()->childAt(row);
 | 
						|
            auto tmpTestResult = tmp->testResult();
 | 
						|
            if (tmpTestResult->id() == id && tmpTestResult->name() == name) {
 | 
						|
                root = tmp;
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if (root == nullptr)
 | 
						|
        return root;
 | 
						|
 | 
						|
    bool needsIntermediate = false;
 | 
						|
    auto predicate = [result, &needsIntermediate](Utils::TreeItem *it) {
 | 
						|
        TestResultItem *currentItem = static_cast<TestResultItem *>(it);
 | 
						|
        return currentItem->testResult()->isDirectParentOf(result, &needsIntermediate);
 | 
						|
    };
 | 
						|
    TestResultItem *parent = root->reverseFindAnyChild(predicate);
 | 
						|
    if (parent) {
 | 
						|
        if (needsIntermediate) {
 | 
						|
            // check if the intermediate is present already
 | 
						|
            if (TestResultItem *intermediate = parent->intermediateFor(item))
 | 
						|
                return intermediate;
 | 
						|
            return parent->createAndAddIntermediateFor(item);
 | 
						|
        }
 | 
						|
        return parent;
 | 
						|
    }
 | 
						|
    return root;
 | 
						|
}
 | 
						|
 | 
						|
/********************************** Filter Model **********************************/
 | 
						|
 | 
						|
TestResultFilterModel::TestResultFilterModel(TestResultModel *sourceModel, QObject *parent)
 | 
						|
    : QSortFilterProxyModel(parent),
 | 
						|
      m_sourceModel(sourceModel)
 | 
						|
{
 | 
						|
    setSourceModel(sourceModel);
 | 
						|
    enableAllResultTypes();
 | 
						|
}
 | 
						|
 | 
						|
void TestResultFilterModel::enableAllResultTypes()
 | 
						|
{
 | 
						|
    m_enabled << Result::Pass << Result::Fail << Result::ExpectedFail
 | 
						|
              << Result::UnexpectedPass << Result::Skip << Result::MessageDebug
 | 
						|
              << Result::MessageWarn << Result::MessageInternal << Result::MessageLocation
 | 
						|
              << Result::MessageFatal << Result::Invalid << Result::BlacklistedPass
 | 
						|
              << Result::BlacklistedFail << Result::BlacklistedXFail << Result::BlacklistedXPass
 | 
						|
              << Result::Benchmark << Result::MessageIntermediate
 | 
						|
              << Result::MessageCurrentTest << Result::MessageTestCaseStart
 | 
						|
              << Result::MessageTestCaseSuccess << Result::MessageTestCaseSuccessWarn
 | 
						|
              << Result::MessageTestCaseFail << Result::MessageTestCaseFailWarn
 | 
						|
              << Result::MessageTestCaseEnd
 | 
						|
              << Result::MessageInfo << Result::MessageSystem << Result::Application;
 | 
						|
    invalidateFilter();
 | 
						|
}
 | 
						|
 | 
						|
void TestResultFilterModel::toggleTestResultType(Result::Type type)
 | 
						|
{
 | 
						|
    if (m_enabled.contains(type)) {
 | 
						|
        m_enabled.remove(type);
 | 
						|
        if (type == Result::MessageInternal)
 | 
						|
            m_enabled.remove(Result::MessageTestCaseEnd);
 | 
						|
        if (type == Result::MessageDebug)
 | 
						|
            m_enabled.remove(Result::MessageInfo);
 | 
						|
        if (type == Result::MessageWarn)
 | 
						|
            m_enabled.remove(Result::MessageSystem);
 | 
						|
    } else {
 | 
						|
        m_enabled.insert(type);
 | 
						|
        if (type == Result::MessageInternal)
 | 
						|
            m_enabled.insert(Result::MessageTestCaseEnd);
 | 
						|
        if (type == Result::MessageDebug)
 | 
						|
            m_enabled.insert(Result::MessageInfo);
 | 
						|
        if (type == Result::MessageWarn)
 | 
						|
            m_enabled.insert(Result::MessageSystem);
 | 
						|
    }
 | 
						|
    invalidateFilter();
 | 
						|
}
 | 
						|
 | 
						|
void TestResultFilterModel::clearTestResults()
 | 
						|
{
 | 
						|
    m_sourceModel->clearTestResults();
 | 
						|
}
 | 
						|
 | 
						|
bool TestResultFilterModel::hasResults()
 | 
						|
{
 | 
						|
    return rowCount(QModelIndex());
 | 
						|
}
 | 
						|
 | 
						|
const TestResult *TestResultFilterModel::testResult(const QModelIndex &index) const
 | 
						|
{
 | 
						|
    return m_sourceModel->testResult(mapToSource(index));
 | 
						|
}
 | 
						|
 | 
						|
bool TestResultFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
 | 
						|
{
 | 
						|
    QModelIndex index = m_sourceModel->index(sourceRow, 0, sourceParent);
 | 
						|
    if (!index.isValid())
 | 
						|
        return false;
 | 
						|
    Result::Type resultType = m_sourceModel->testResult(index)->result();
 | 
						|
    switch (resultType) {
 | 
						|
    case Result::MessageTestCaseSuccess:
 | 
						|
        return m_enabled.contains(Result::Pass);
 | 
						|
    case Result::MessageTestCaseFail:
 | 
						|
    case Result::MessageTestCaseSuccessWarn:
 | 
						|
    case Result::MessageTestCaseFailWarn:
 | 
						|
        return acceptTestCaseResult(index);
 | 
						|
    default:
 | 
						|
        return m_enabled.contains(resultType);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
bool TestResultFilterModel::acceptTestCaseResult(const QModelIndex &srcIndex) const
 | 
						|
{
 | 
						|
    for (int row = 0, count = m_sourceModel->rowCount(srcIndex); row < count; ++row) {
 | 
						|
        const QModelIndex &child = m_sourceModel->index(row, 0, srcIndex);
 | 
						|
        Result::Type type = m_sourceModel->testResult(child)->result();
 | 
						|
        if (type == Result::MessageTestCaseSuccess)
 | 
						|
            type = Result::Pass;
 | 
						|
        if (type == Result::MessageTestCaseFail || type == Result::MessageTestCaseFailWarn
 | 
						|
                || type == Result::MessageTestCaseSuccessWarn) {
 | 
						|
            if (acceptTestCaseResult(child))
 | 
						|
                return true;
 | 
						|
        } else if (m_enabled.contains(type)) {
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
} // namespace Internal
 | 
						|
} // namespace Autotest
 |