try {
                std::rethrow_exception(e);
            }
            catch (exception const &e) {
                std::cout << "Caught asynchronous SYCL exception during SYR:\n"
                          << e.what() << std::endl;
                print_error_code(e);
            }

/*******************************************************************************
*
*
*
*******************************************************************************/

#include <algorithm>
#include <complex>
#include <cstdlib>
#include <iostream>
#include <limits>
#include <vector>

#include <CL/sycl.hpp>
#include "cblas.h"
#include "oneapi/mkl.hpp"
#include "oneapi/mkl/detail/config.hpp"
#include "onemkl_blas_helper.hpp"
#include "reference_blas_templates.hpp"
#include "test_common.hpp"
#include "test_helper.hpp"

#include <gtest/gtest.h>

using namespace cl::sycl;
using std::vector;

extern std::vector<cl::sycl::device *> devices;

namespace {

template <typename fp>
int test(device *dev, oneapi::mkl::layout layout, oneapi::mkl::uplo upper_lower, int n, fp alpha,
         int incx, int lda) {
    // Prepare data.
    vector<fp> x, A_ref, A;
    rand_vector(x, n, incx);
    rand_matrix(A, layout, oneapi::mkl::transpose::nontrans, n, n, lda);
    A_ref = A;

    // Call Reference SYR.
    const int n_ref = n, incx_ref = incx, lda_ref = lda;
    using fp_ref = typename ref_type_info<fp>::type;

    ::syr(convert_to_cblas_layout(layout), convert_to_cblas_uplo(upper_lower), &n_ref,
          (fp_ref *)&alpha, (fp_ref *)x.data(), &incx_ref, (fp_ref *)A_ref.data(), &lda_ref);

    // Call DPC++ SYR.

    // Catch asynchronous exceptions.
    auto exception_handler = [](exception_list exceptions) {
        for (std::exception_ptr const &e : exceptions) {

        }
    };

    queue main_queue(*dev, exception_handler);

    buffer<fp, 1> x_buffer = make_buffer(x);
    buffer<fp, 1> A_buffer = make_buffer(A);

    try {
#ifdef CALL_RT_API
        switch (layout) {
            case oneapi::mkl::layout::column_major:
                oneapi::mkl::blas::column_major::syr(main_queue, upper_lower, n, alpha, x_buffer,
                                                     incx, A_buffer, lda);
                break;
            case oneapi::mkl::layout::row_major:
                oneapi::mkl::blas::row_major::syr(main_queue, upper_lower, n, alpha, x_buffer, incx,
                                                  A_buffer, lda);
                break;
            default: break;
        }
#else
        switch (layout) {
            case oneapi::mkl::layout::column_major:
                TEST_RUN_CT_SELECT(main_queue, oneapi::mkl::blas::column_major::syr, upper_lower, n,
                                   alpha, x_buffer, incx, A_buffer, lda);
                break;
            case oneapi::mkl::layout::row_major:
                TEST_RUN_CT_SELECT(main_queue, oneapi::mkl::blas::row_major::syr, upper_lower, n,
                                   alpha, x_buffer, incx, A_buffer, lda);
                break;
            default: break;
        }
#endif
    }
    catch (exception const &e) {
        std::cout << "Caught synchronous SYCL exception during SYR:\n" << e.what() << std::endl;
        print_error_code(e);
    }

    catch (const oneapi::mkl::unimplemented &e) {
        return test_skipped;
    }

    catch (const std::runtime_error &error) {
        std::cout << "Error raised during execution of SYR:\n" << error.what() << std::endl;
    }

    // Compare the results of reference implementation and DPC++ implementation.
    auto A_accessor = A_buffer.template get_access<access::mode::read>();
    bool good = check_equal_matrix(A_accessor, A_ref, layout, n, n, lda, n, std::cout);

    return (int)good;
}

class SyrTests
        : public ::testing::TestWithParam<std::tuple<cl::sycl::device *, oneapi::mkl::layout>> {};

TEST_P(SyrTests, RealSinglePrecision) {
    float alpha(2.0);
    EXPECT_TRUEORSKIP(test<float>(std::get<0>(GetParam()), std::get<1>(GetParam()),
                                  oneapi::mkl::uplo::lower, 30, alpha, 2, 42));
    EXPECT_TRUEORSKIP(test<float>(std::get<0>(GetParam()), std::get<1>(GetParam()),
                                  oneapi::mkl::uplo::upper, 30, alpha, 2, 42));
    EXPECT_TRUEORSKIP(test<float>(std::get<0>(GetParam()), std::get<1>(GetParam()),
                                  oneapi::mkl::uplo::lower, 30, alpha, -2, 42));
    EXPECT_TRUEORSKIP(test<float>(std::get<0>(GetParam()), std::get<1>(GetParam()),
                                  oneapi::mkl::uplo::upper, 30, alpha, -2, 42));
    EXPECT_TRUEORSKIP(test<float>(std::get<0>(GetParam()), std::get<1>(GetParam()),
                                  oneapi::mkl::uplo::lower, 30, alpha, 1, 42));
    EXPECT_TRUEORSKIP(test<float>(std::get<0>(GetParam()), std::get<1>(GetParam()),
                                  oneapi::mkl::uplo::upper, 30, alpha, 1, 42));
}
TEST_P(SyrTests, RealDoublePrecision) {
    double alpha(2.0);
    EXPECT_TRUEORSKIP(test<double>(std::get<0>(GetParam()), std::get<1>(GetParam()),
                                   oneapi::mkl::uplo::lower, 30, alpha, 2, 42));
    EXPECT_TRUEORSKIP(test<double>(std::get<0>(GetParam()), std::get<1>(GetParam()),
                                   oneapi::mkl::uplo::upper, 30, alpha, 2, 42));
    EXPECT_TRUEORSKIP(test<double>(std::get<0>(GetParam()), std::get<1>(GetParam()),
                                   oneapi::mkl::uplo::lower, 30, alpha, -2, 42));
    EXPECT_TRUEORSKIP(test<double>(std::get<0>(GetParam()), std::get<1>(GetParam()),
                                   oneapi::mkl::uplo::upper, 30, alpha, -2, 42));
    EXPECT_TRUEORSKIP(test<double>(std::get<0>(GetParam()), std::get<1>(GetParam()),
                                   oneapi::mkl::uplo::lower, 30, alpha, 1, 42));
    EXPECT_TRUEORSKIP(test<double>(std::get<0>(GetParam()), std::get<1>(GetParam()),
                                   oneapi::mkl::uplo::upper, 30, alpha, 1, 42));
}

INSTANTIATE_TEST_SUITE_P(SyrTestSuite, SyrTests,
                         ::testing::Combine(testing::ValuesIn(devices),
                                            testing::Values(oneapi::mkl::layout::column_major,
                                                            oneapi::mkl::layout::row_major)),
                         ::LayoutDeviceNamePrint());

} // anonymous namespace


try {
            exists = boost::filesystem::exists(absoluteLogpath);
        } catch (boost::filesystem::filesystem_error& e) {
            return Status(ErrorCodes::FileNotOpen,
                          mongoutils::str::stream() << "Failed probe for \"" << absoluteLogpath
                                                    << "\": "
                                                    << e.code().message());
        }

/**
*
*/

#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kControl

#include "mongo/platform/basic.h"

#include "mongo/db/initialize_server_global_state.h"

#include <boost/filesystem/operations.hpp>
#include <iostream>
#include <memory>
#include <signal.h>

#ifndef _WIN32
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <syslog.h>
#endif

#include "mongo/base/init.h"
#include "mongo/client/sasl_client_authenticate.h"
#include "mongo/config.h"
#include "mongo/db/auth/authorization_manager.h"
#include "mongo/db/auth/authorization_manager_global.h"
#include "mongo/db/auth/internal_user_auth.h"
#include "mongo/db/auth/security_key.h"
#include "mongo/db/server_options.h"
#include "mongo/db/server_parameters.h"
#include "mongo/logger/console_appender.h"
#include "mongo/logger/logger.h"
#include "mongo/logger/message_event.h"
#include "mongo/logger/message_event_utf8_encoder.h"
#include "mongo/logger/ramlog.h"
#include "mongo/logger/rotatable_file_appender.h"
#include "mongo/logger/rotatable_file_manager.h"
#include "mongo/logger/rotatable_file_writer.h"
#include "mongo/logger/syslog_appender.h"
#include "mongo/platform/process_id.h"
#include "mongo/util/log.h"
#include "mongo/util/mongoutils/str.h"
#include "mongo/util/net/listen.h"
#include "mongo/util/net/ssl_manager.h"
#include "mongo/util/processinfo.h"
#include "mongo/util/quick_exit.h"
#include "mongo/util/signal_handlers_synchronous.h"

#if defined(__APPLE__)
#include <TargetConditionals.h>
#endif

namespace fs = boost::filesystem;

namespace mongo {

using std::cerr;
using std::cout;
using std::endl;

#ifndef _WIN32
// support for exit value propagation with fork
void launchSignal(int sig) {
    if (sig == SIGUSR2) {
        ProcessId cur = ProcessId::getCurrent();

        if (cur == serverGlobalParams.parentProc || cur == serverGlobalParams.leaderProc) {
            // signal indicates successful start allowing us to exit
            quickExit(0);
        }
    }
}

void signalForkSuccess() {
    if (serverGlobalParams.doFork) {
        // killing leader will propagate to parent
        verify(kill(serverGlobalParams.leaderProc.toNative(), SIGUSR2) == 0);
    }
}
#endif

static bool forkServer() {
#if !defined(_WIN32) && !(defined(__APPLE__) && TARGET_OS_TV)
    if (serverGlobalParams.doFork) {
        fassert(16447, !serverGlobalParams.logpath.empty() || serverGlobalParams.logWithSyslog);

        cout.flush();
        cerr.flush();

		//获取进程号
        serverGlobalParams.parentProc = ProcessId::getCurrent();

        // clear signal mask so that SIGUSR2 will always be caught and we can clean up the original
        // parent process
        clearSignalMask();

        // facilitate clean exit when child starts successfully
        verify(signal(SIGUSR2, launchSignal) != SIG_ERR);

        cout << "about to fork child process, waiting until server is ready for connections."
             << endl;

        pid_t child1 = fork();
        if (child1 == -1) {
            cout << "ERROR: stage 1 fork() failed: " << errnoWithDescription();
            quickExit(EXIT_ABRUPT);
        } else if (child1) { //父进程
            // this is run in the original parent process
            int pstat;
            waitpid(child1, &pstat, 0);

            if (WIFEXITED(pstat)) {
                if (WEXITSTATUS(pstat)) {
                    cout << "ERROR: child process failed, exited with error number "
                         << WEXITSTATUS(pstat) << endl
                         << "To see additional information in this output, start without "
                         << "the \"--fork\" option." << endl;
                } else {
                    cout << "child process started successfully, parent exiting" << endl;
                }

                quickExit(WEXITSTATUS(pstat));
            }

            quickExit(50);
        }

        if (chdir("/") < 0) {
            cout << "Cant chdir() while forking server process: " << strerror(errno) << endl;
            quickExit(-1);
        }
        setsid();

        serverGlobalParams.leaderProc = ProcessId::getCurrent();

        pid_t child2 = fork();
        if (child2 == -1) {
            cout << "ERROR: stage 2 fork() failed: " << errnoWithDescription();
            quickExit(EXIT_ABRUPT);
        } else if (child2) {
            // this is run in the middle process
            int pstat;
            cout << "forked process: " << child2 << endl;
            waitpid(child2, &pstat, 0);

            if (WIFEXITED(pstat)) {
                quickExit(WEXITSTATUS(pstat));
            }

            quickExit(51);
        }

        // this is run in the final child process (the server)

        FILE* f = freopen("/dev/null", "w", stdout);
        if (f == NULL) {
            cout << "Cant reassign stdout while forking server process: " << strerror(errno)
                 << endl;
            return false;
        }

        f = freopen("/dev/null", "w", stderr);
        if (f == NULL) {
            cout << "Cant reassign stderr while forking server process: " << strerror(errno)
                 << endl;
            return false;
        }

        f = freopen("/dev/null", "r", stdin);
        if (f == NULL) {
            cout << "Cant reassign stdin while forking server process: " << strerror(errno) << endl;
            return false;
        }
    }
#endif  // !defined(_WIN32)
    return true;
}

/*
*/
//s/server.cpp 和 db.cpp中调用
void forkServerOrDie() {
    if (!forkServer())
        quickExit(EXIT_FAILURE);
}

MONGO_INITIALIZER_GENERAL(ServerLogRedirection,
                          ("GlobalLogManager", "EndStartupOptionHandling", "ForkServer"),
                          ("default"))
(InitializerContext*) {
    using logger::LogManager;
    using logger::MessageEventEphemeral;
    using logger::MessageEventDetailsEncoder;
    using logger::MessageEventWithContextEncoder;
    using logger::MessageLogDomain;
    using logger::RotatableFileAppender;
    using logger::StatusWithRotatableFileWriter;

    if (serverGlobalParams.logWithSyslog) {
#ifdef _WIN32
        return Status(ErrorCodes::InternalError,
                      "Syslog requested in Windows build; command line processor logic error");
#else
        using logger::SyslogAppender;
        StringBuilder sb;
        sb << serverGlobalParams.binaryName << "." << serverGlobalParams.port;
        openlog(strdup(sb.str().c_str()), LOG_PID | LOG_CONS, serverGlobalParams.syslogFacility);
        LogManager* manager = logger::globalLogManager();
        manager->getGlobalDomain()->clearAppenders();
        manager->getGlobalDomain()->attachAppender(MessageLogDomain::AppenderAutoPtr(
            new SyslogAppender<MessageEventEphemeral>(new logger::MessageEventWithContextEncoder)));
        manager->getNamedDomain("javascriptOutput")
            ->attachAppender(
                MessageLogDomain::AppenderAutoPtr(new SyslogAppender<MessageEventEphemeral>(
                    new logger::MessageEventWithContextEncoder)));
#endif  // defined(_WIN32)
    } else if (!serverGlobalParams.logpath.empty()) {
		StringBuilder sb;
        sb << serverGlobalParams.binaryName << "." << serverGlobalParams.port << "ALLSQL";
        openlog(strdup(sb.str().c_str()), LOG_PID | LOG_CONS, LOG_USER);

        fassert(16448, !serverGlobalParams.logWithSyslog);
        std::string absoluteLogpath =
            boost::filesystem::absolute(serverGlobalParams.logpath, serverGlobalParams.cwd)
                .string();

        bool exists;

        if (exists) {
            if (boost::filesystem::is_directory(absoluteLogpath)) {
                return Status(
                    ErrorCodes::FileNotOpen,
                    mongoutils::str::stream() << "logpath \"" << absoluteLogpath
                                              << "\" should name a file, not a directory.");
            }

            if (!serverGlobalParams.logAppend && boost::filesystem::is_regular(absoluteLogpath)) {
                std::string renameTarget = absoluteLogpath + "." + terseCurrentTime(false);
                boost::system::error_code ec;
                boost::filesystem::rename(absoluteLogpath, renameTarget, ec);
                if (!ec) {
                    log() << "log file \"" << absoluteLogpath << "\" exists; moved to \""
                          << renameTarget << "\".";
                } else {
                    return Status(ErrorCodes::FileRenameFailed,
                                  mongoutils::str::stream()
                                      << "Could not rename preexisting log file \""
                                      << absoluteLogpath
                                      << "\" to \""
                                      << renameTarget
                                      << "\"; run with --logappend or manually remove file: "
                                      << ec.message());
                }
            }
        }

        StatusWithRotatableFileWriter writer = logger::globalRotatableFileManager()->openFile(
            absoluteLogpath, serverGlobalParams.logAppend);
        if (!writer.isOK()) {
            return writer.getStatus();
        }

        LogManager* manager = logger::globalLogManager();
        manager->getGlobalDomain()->clearAppenders();
        manager->getGlobalDomain()->attachAppender(
            MessageLogDomain::AppenderAutoPtr(new RotatableFileAppender<MessageEventEphemeral>(
                new MessageEventDetailsEncoder, writer.getValue())));
        manager->getNamedDomain("javascriptOutput")
            ->attachAppender(
                MessageLogDomain::AppenderAutoPtr(new RotatableFileAppender<MessageEventEphemeral>(
                    new MessageEventDetailsEncoder, writer.getValue())));

        if (serverGlobalParams.logAppend && exists) {
            log() << "***** SERVER RESTARTED *****";
            Status status = logger::RotatableFileWriter::Use(writer.getValue()).status();
            if (!status.isOK())
                return status;
        }
    } else {
        logger::globalLogManager()
            ->getNamedDomain("javascriptOutput")
            ->attachAppender(MessageLogDomain::AppenderAutoPtr(
                new logger::ConsoleAppender<MessageEventEphemeral>(
                    new MessageEventDetailsEncoder)));
    }

    logger::globalLogDomain()->attachAppender(
        logger::MessageLogDomain::AppenderAutoPtr(new RamLogAppender(RamLog::get("global"))));

    return Status::OK();
}

/**
 * atexit handler to terminate the process before static destructors run.
 *
 * Mongo server processes cannot safely call ::exit() or std::exit(), but
 * some third-party libraries may call one of those functions.  In that
 * case, to avoid static-destructor problems in the server, this exits the
 * process immediately with code EXIT_FAILURE.
 *
 * TODO: Remove once exit() executes safely in mongo server processes.
 */
static void shortCircuitExit() {
    quickExit(EXIT_FAILURE);
}

MONGO_INITIALIZER(RegisterShortCircuitExitHandler)(InitializerContext*) {
    if (std::atexit(&shortCircuitExit) != 0)
        return Status(ErrorCodes::InternalError, "Failed setting short-circuit exit handler.");
    return Status::OK();
}

// On non-windows platforms, drop rwx for group and other unless the
// user has opted into using the system umask. To do so, we first read
// out the current umask (by temporarily setting it to
// no-permissions), and then or the returned umask with the
// restrictions we want to apply and set it back. The overall effect
// is to set the bits for 'other' and 'group', but leave umask bits
#ifndef _WIN32
namespace {
MONGO_EXPORT_STARTUP_SERVER_PARAMETER(honorSystemUmask, bool, false);
MONGO_INITIALIZER(MungeUmask)(InitializerContext*) {
    if (!honorSystemUmask) {
        umask(umask(S_IRWXU | S_IRWXG | S_IRWXO) | S_IRWXG | S_IRWXO);
    }
    return Status::OK();
}
}  // namespace
#endif

bool initializeServerGlobalState() {
    Listener::globalTicketHolder.resize(serverGlobalParams.maxConns).transitional_ignore();

#ifndef _WIN32
    if (!fs::is_directory(serverGlobalParams.socket)) {
        cout << serverGlobalParams.socket << " must be a directory" << endl;
        return false;
    }
#endif

    if (!serverGlobalParams.pidFile.empty()) {
        if (!writePidFile(serverGlobalParams.pidFile)) {
            // error message logged in writePidFile
            return false;
        }
    }

    int clusterAuthMode = serverGlobalParams.clusterAuthMode.load();
    if (!serverGlobalParams.keyFile.empty() &&
        clusterAuthMode != ServerGlobalParams::ClusterAuthMode_x509) {
        if (!setUpSecurityKey(serverGlobalParams.keyFile)) {
            // error message printed in setUpPrivateKey
            return false;
        }
    }

    // Auto-enable auth unless we are in mixed auth/no-auth or clusterAuthMode was not provided.
    // clusterAuthMode defaults to "keyFile" if a --keyFile parameter is provided.
    if (clusterAuthMode != ServerGlobalParams::ClusterAuthMode_undefined &&
        !serverGlobalParams.transitionToAuth) {
        getGlobalAuthorizationManager()->setAuthEnabled(true);
    }

#ifdef MONGO_CONFIG_SSL

    if (clusterAuthMode == ServerGlobalParams::ClusterAuthMode_x509 ||
        clusterAuthMode == ServerGlobalParams::ClusterAuthMode_sendX509) {
        setInternalUserAuthParams(
            BSON(saslCommandMechanismFieldName
                 << "MONGODB-X509"
                 << saslCommandUserDBFieldName
                 << "$external"
                 << saslCommandUserFieldName
                 << getSSLManager()->getSSLConfiguration().clientSubjectName));
    }
#endif
    return true;
}

}  // namespace mongo


try
            {
                vtRouterEndpoint = Application::getCommunicator()->getEndpoint4All(sRouteObj);
                break;
            }
            catch (const exception & ex)
            {
                TLOGERROR("TimerThread::" << __FUNCTION__ << " getEndpoint4All exception: " << ex.what() << endl);
            }

/**
* Tencent is pleased to support the open source community by making DCache available.
*/
#include "TimerThread.h"
#include "RouterHandle.h"
#include "CacheServer.h"
#include "ControlAck.h"

void TimerThread::init(const string &sConf)
{
    _config = sConf;
    _tcConf.parseFile(_config);

    _syncRouteInterval = TC_Common::strto<int>(_tcConf["/Main/Router<SyncInterval>"]);
    _binlogHeartbeatInterval = TC_Common::strto<int>(_tcConf.get("/Main/BinLog<HeartbeatInterval>", "600"));
    _binlogFile = _tcConf["/Main/BinLog<LogFile>"];
    _keyBinlogFile = _tcConf["/Main/BinLog<LogFile>"] + "key";
    _moduleName = _tcConf["/Main<ModuleName>"];
    string sRecordBinLog = _tcConf.get("/Main/BinLog<Record>", "Y");
    _recordBinLog = (sRecordBinLog == "Y" || sRecordBinLog == "y") ? true : false;

    string sRecordKeyBinLog = _tcConf.get("/Main/BinLog<KeyRecord>", "N");
    _recordKeyBinLog = (sRecordKeyBinLog == "Y" || sRecordKeyBinLog == "y") ? true : false;

    _shmNum = TC_Common::strto<unsigned int>(_tcConf.get("/Main/Cache<JmemNum>", "10"));

    _srp_dirtyCnt = Application::getCommunicator()->getStatReport()->createPropertyReport("CountOfDirtyRecords", PropertyReport::avg());
    _srp_hitcount = Application::getCommunicator()->getStatReport()->createPropertyReport("CacheHitRatio", PropertyReport::avg());

    _srp_memSize = Application::getCommunicator()->getStatReport()->createPropertyReport("CacheMemSize_MB", PropertyReport::avg());
    _srp_memInUse = Application::getCommunicator()->getStatReport()->createPropertyReport("DataMemUsage", PropertyReport::avg());
    _srp_dirtyRatio = Application::getCommunicator()->getStatReport()->createPropertyReport("ProportionOfDirtyRecords", PropertyReport::avg());
    _srp_chunksOnceEle = Application::getCommunicator()->getStatReport()->createPropertyReport("ChunkUsedPerRecord", PropertyReport::avg());
    _srp_elementCount = Application::getCommunicator()->getStatReport()->createPropertyReport("TotalCountOfRecords", PropertyReport::avg());
    _srp_onlykeyCount = Application::getCommunicator()->getStatReport()->createPropertyReport("CountOfOnlyKey", PropertyReport::avg());
    _srp_maxJmemUsage = Application::getCommunicator()->getStatReport()->createPropertyReport("MaxMemUsageOfJmem", PropertyReport::avg());
    if (_srp_dirtyCnt == 0 || _srp_hitcount == 0 || _srp_memSize == 0 || _srp_memInUse == 0 || _srp_dirtyRatio == 0 ||
        _srp_chunksOnceEle == 0 || _srp_elementCount == 0 || _srp_onlykeyCount == 0 || _srp_maxJmemUsage == 0)
    {
        TLOGERROR("TimerThread::init createPropertyReport error" << endl);
        assert(false);
    }

    _downgradeTimeout = TC_Common::strto<int>(_tcConf.get("/Main/Cache<DowngradeTimeout>", "30"));

    TLOGDEBUG("TimerThread::init succ" << endl);
}

void TimerThread::reload()
{
    _tcConf.parseFile(_config);

    _syncRouteInterval = TC_Common::strto<int>(_tcConf["/Main/Router<SyncInterval>"]);
    _downgradeTimeout = TC_Common::strto<int>(_tcConf.get("/Main/Cache<DowngradeTimeout>", "30"));
    TLOGDEBUG("TimerThread::reload succ" << endl);
}

void TimerThread::createThread()
{
    //创建线程
    pthread_t thread;

    if (!_isStart)
    {
        _isStart = true;
        if (pthread_create(&thread, NULL, Run, (void*)this) != 0)
        {
            throw runtime_error("Create TimerThread fail");
        }
    }
}

void* TimerThread::Run(void* arg)
{
    pthread_detach(pthread_self());
    TimerThread* pthis = (TimerThread*)arg;
    pthis->setRuning(true);

    time_t tLastRoute = 0;
    time_t tLastReport = 0;
    time_t tLastBinlogHeartbeat = 0;
    time_t tLastCheckConnectHb = TC_TimeProvider::getInstance()->getNow();
    uint64_t connectHbCount = RouterHandle::getInstance()->getSlaveHbCount();

    while (pthis->isStart())
    {
        time_t tNow = TC_TimeProvider::getInstance()->getNow();
        if (tNow - tLastRoute >= pthis->_syncRouteInterval)
        {
            if (g_app.gstat()->isSlaveCreating() == true)
            {
                pthis->_isInSlaveCreating = true;
                if (g_app.gstat()->serverType() == MASTER)
                {
                    ostringstream os;
                    os << "server changed from SLAVE to MASTER when in data creating status";
                    TARS_NOTIFY_ERROR(os.str());
                    TLOGERROR(os.str() << endl);
                }
            }
            else
            {
                pthis->_isInSlaveCreating = false;
                pthis->syncRoute();
                tLastRoute = tNow;
            }
        }
        if (tNow - tLastBinlogHeartbeat >= pthis->_binlogHeartbeatInterval)
        {
            TBinLogEncode logEncode;
            string sActive = logEncode.EncodeActive();
            sActive += "\n";

            if (pthis->_recordBinLog)
            {
                CacheServer::WriteToFile(sActive, pthis->_binlogFile);
                if (g_app.gstat()->serverType() == MASTER)
                    g_app.gstat()->setBinlogTime(0, TNOW);
            }

            if (pthis->_recordKeyBinLog)
            {

                CacheServer::WriteToFile(sActive, pthis->_keyBinlogFile);
                if (g_app.gstat()->serverType() == MASTER)
                    g_app.gstat()->setBinlogTime(0, TNOW);
            }
            tLastBinlogHeartbeat = tNow;
        }
        for (int i = 1; i <= g_app.gstat()->hitIndex(); i++)
        {
            HitCount count;
            g_app.gstat()->getHitCnt(i, count);
            if (tNow - count.t >= 60 && count.totalCount != 0)
            {
                pthis->_srp_hitcount->report((int)((float)count.hitCount / count.totalCount * 100));
                g_app.gstat()->resetHitCnt(i);
            }
        }
        if (tNow - tLastReport >= 60)
        {
            int biggest = 0;
            for (unsigned int i = 0; i < pthis->_shmNum; i++)
            {
                int tmp = 0;
                g_sHashMap.getUseRatio(i, tmp);
                if (tmp > biggest)
                    biggest = tmp;
            }
            pthis->_srp_maxJmemUsage->report(biggest);

            pthis->_srp_memSize->report((int)((float)g_sHashMap.getMemSize() / 1024 / 1024));
            pthis->_srp_memInUse->report((int)((float)g_sHashMap.getUsedDataMem() / g_sHashMap.getDataMemSize() * 100));
            if (g_sHashMap.size() == 0)
            {
                pthis->_srp_dirtyRatio->report(0);
                pthis->_srp_chunksOnceEle->report(0);
            }
            else
            {
                pthis->_srp_dirtyRatio->report((int)((float)g_sHashMap.dirtyCount() / g_sHashMap.size() * 100));
                pthis->_srp_chunksOnceEle->report((int)((float)g_sHashMap.usedChunkCount() / g_sHashMap.size()));
            }
            pthis->_srp_dirtyCnt->report(g_sHashMap.dirtyCount());
            pthis->_srp_elementCount->report(g_sHashMap.size() - g_sHashMap.onlyKeyCount());
            pthis->_srp_onlykeyCount->report(g_sHashMap.onlyKeyCount());
            tLastReport = tNow;
        }

        //check slave connect heartbeat
        if (pthis->_downgradeTimeout > 0 && (tNow - tLastCheckConnectHb) >= pthis->_downgradeTimeout)
        {
            if (g_app.gstat()->serverType() == MASTER)
            {
                uint64_t iCount = RouterHandle::getInstance()->getSlaveHbCount();
                if (iCount == connectHbCount)
                {
                    pthis->handleConnectHbTimeout();
                }

                connectHbCount = iCount;

            }

            tLastCheckConnectHb = tNow;
        }
        sleep(1);
    }
    pthis->setRuning(false);
    pthis->setStart(false);
    return NULL;
}

void TimerThread::syncRoute()
{

    RouterHandle::getInstance()->syncRoute();
}

size_t TimerThread::getDirtyNum()
{
    size_t iDirty = g_sHashMap.dirtyCount();
    return iDirty;
}

enum ServerType TimerThread::getType()
{
    string sServerName = ServerConfig::Application + "." + ServerConfig::ServerName;

    GroupInfo group;
    g_route_table.getGroup(sServerName, group);

    if (group.masterServer == sServerName)
    {
        return MASTER;
    }
    return SLAVE;
}

void TimerThread::handleConnectHbTimeout()
{
    int ret = 0;
    try
    {
        //检测是否能正常访问router
        bool succ = false;
        string sRouteObj = _tcConf["/Main/Router<ObjName>"];
        RouterPrx routePrx;
        vector<TC_Endpoint> vtRouterEndpoint;
        int reTry = 2;
        while (reTry-- > 0)
        {

        }

        string serverName = ServerConfig::Application + "." + ServerConfig::ServerName;
        for (size_t i = 0; i < vtRouterEndpoint.size(); i++)
        {
            string sRouteProxy = sRouteObj + "@tcp -h " + vtRouterEndpoint[i].getHost() + " -p " + TC_Common::tostr(vtRouterEndpoint[i].getPort());
            reTry = 2;
            while (reTry-- > 0)
            {
                try
                {
                    routePrx = Application::getCommunicator()->stringToProxy<RouterPrx>(sRouteProxy);
                    ret = routePrx->heartBeatReport(_moduleName, g_app.gstat()->groupName(), serverName);
                    succ = true;
                    FDLOG("switch") << "TimerThread::" << __FUNCTION__ << " heartBeatReport succ to " << sRouteProxy << endl;
                    break;
                }
                catch (const exception & ex)
                {
                    TLOGERROR("TimerThread::" << __FUNCTION__ << " heartBeatReport exception again: " << ex.what() << endl);
                }
            }

            if (succ)
                break;
        }

        FDLOG("switch") << "TimerThread::handleConnectHbTimeout connect timeout, vtRouterEndpoint size:"
                        << vtRouterEndpoint.size() << "|invoke router:" << (succ ? "succ" : "fail") << endl;

        if (!succ)
        {
            ret = RouterHandle::getInstance()->masterDowngrade();
            if (ret != 0)
            {
                TLOGERROR("TimerThread::handleConnectHbTimeout masterDowngrade error:" << ret << endl);
                assert(false);
            }

            FDLOG("switch") << "TimerThread::handleConnectHbTimeout masterDowngrade succ." << endl;
        }
    }
    catch (exception &ex)
    {
        TLOGERROR("[TimerThread::handleConnectHbTimeout] exception:" << ex.what() << endl);
    }
    catch (...)
    {
        TLOGERROR("[TimerThread::handleConnectHbTimeout] unknown exception." << endl);
    }
}

void CreateBinlogFileThread::createThread()
{
    //创建线程
    pthread_t thread;

    if (pthread_create(&thread, NULL, Run, (void*)this) != 0)
    {
        throw runtime_error("Create CreateBinlogFileThread fail");
    }
}

void* CreateBinlogFileThread::Run(void* arg)
{
    TLOGDEBUG("[CreateBinlogFileThread::Run] start!" << endl);

    pthread_detach(pthread_self());

    //读取配置文件
    TC_Config config;
    config.parseFile(ServerConfig::BasePath + "CacheServer.conf");

    string sBinlogFile = config["/Main/BinLog<LogFile>"];
    string sKeyBinlogFile = config["/Main/BinLog<LogFile>"] + "key";

    string sRecordBinLog = config.get("/Main/BinLog<Record>", "Y");
    bool bRecordBinLog = (sRecordBinLog == "Y" || sRecordBinLog == "y") ? true : false;

    string sRecordKeyBinLog = config.get("/Main/BinLog<KeyRecord>", "N");
    bool bKeyRecordBinLog = (sRecordKeyBinLog == "Y" || sRecordKeyBinLog == "y") ? true : false;

    //既不打普通binlog，也不打keybinlog，就直接退出
    if (!bRecordBinLog && !bKeyRecordBinLog)
        return NULL;

    string path = ServerConfig::LogPath + "/" + ServerConfig::Application + "/" + ServerConfig::ServerName + "/" + ServerConfig::Application + "." + ServerConfig::ServerName + "_";

    char str[128];
    struct tm ptr;
    time_t nowSec;
    string normalBinlogPath, keyBinlogPath;

    static string sTimeStr;
    while (!((CreateBinlogFileThread*)arg)->_stop)
    {
        memset(str, '\0', 128);
        nowSec = time(NULL);
        ptr = *(localtime(&nowSec));
        strftime(str, 80, "%Y%m%d%H", &ptr);
        string tmpStr = str;

        //时间跳到下一个小时了，就要加锁生成文件
        if (tmpStr != sTimeStr)
        {
            TLOGDEBUG("[CreateBinlogFileThread::Run] create file!" << endl);

            if (bRecordBinLog)
            {
                normalBinlogPath = path + sBinlogFile + "_" + str + ".log";
                int ret = g_app.createBinLogFile(normalBinlogPath, false);
                assert(ret == 0);
            }

            if (bKeyRecordBinLog)
            {
                keyBinlogPath = path + sKeyBinlogFile + "_" + str + ".log";
                int ret = g_app.createBinLogFile(keyBinlogPath, true);
                assert(ret == 0);
            }

            sTimeStr = str;
        }

        sleep(1);
    }

    return NULL;
}

void HeartBeatThread::createThread()
{
    //创建线程
    pthread_t thread;

    if (pthread_create(&thread, NULL, Run, (void*)this) != 0)
    {
        throw runtime_error("Create HeartBeatThread fail");
    }
}

void* HeartBeatThread::Run(void* arg)
{
    TLOGDEBUG("[HeartBeatThread::Run] start!" << endl);

    pthread_detach(pthread_self());

    HeartBeatThread* pThis = (HeartBeatThread*)arg;
    //读取配置文件
    TC_Config config;
    config.parseFile(ServerConfig::BasePath + "CacheServer.conf");

    int interval = TC_Common::strto<int>(config.get("/Main<RouterHeartbeatInterval>", "1000"));

    string masterAddr = RouterHandle::getInstance()->getConnectHbAddr();
    ControlAckPrx connectPrx = NULL;
    if (masterAddr.length() > 0)
    {
        connectPrx = Application::getCommunicator()->stringToProxy<ControlAckPrx>(masterAddr);
        TLOGDEBUG("HeartBeatThread::Run, connect hb master obj:" << masterAddr << endl);
    }

    while (!pThis->_stop)
    {
        //report heartBeat to Router.
        RouterHandle::getInstance()->heartBeat();

        //report connect heartbeat to master
        if (pThis->_enableConHb)
        {
            try
            {
                string tmpAddr = RouterHandle::getInstance()->getConnectHbAddr();
                if (!tmpAddr.empty() && masterAddr != tmpAddr)
                {
                    connectPrx = Application::getCommunicator()->stringToProxy<ControlAckPrx>(tmpAddr);
                    masterAddr = tmpAddr;
                    TLOGDEBUG("HeartBeatThread::Run, connect hb master change to obj:" << masterAddr << endl);
                }

                if (connectPrx)
                    connectPrx->connectHb();
            }
            catch (const exception & ex)
            {
                TLOGERROR("HeartBeatThread::" << __FUNCTION__ << " connect hb exception: " << ex.what() << endl);
            }
        }

        usleep(interval * 1000);
    }

    return NULL;
}


try
    {
        stop_server();
    }
    catch (...)
    {
    }

#include "openssh_fixture.hpp"

#include "swish/connection/session_pool.hpp"

#include <boost/assign/list_of.hpp>
#include <boost/date_time/posix_time/posix_time_duration.hpp>
#include <boost/foreach.hpp>
#include <boost/io/detail/quoted_manip.hpp>
#include <boost/optional.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/locale.hpp>
#include <boost/process/context.hpp>
#include <boost/process/environment.hpp>
#include <boost/process/operations.hpp> // find_executable_in_path
#include <boost/process/pistream.hpp>
#include <boost/process/self.hpp>
#include <boost/process/stream_behavior.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/thread/thread.hpp> // this_thread

#include <iterator>
#include <map>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>

using boost::assign::list_of;
using boost::filesystem::path;
using boost::io::quoted;
using boost::lexical_cast;
using boost::locale::conv::to_utf;
using boost::locale::generator;
using boost::locale::util::get_system_locale;
using boost::optional;
using boost::process::behavior::pipe;
using boost::process::child;
using boost::process::context;
using boost::process::environment;
using boost::process::find_executable_in_path;
using boost::process::pistream;
using boost::process::self;
using boost::process::stderr_id;
using boost::process::stdout_id;

using std::locale;
using std::map;
using std::ostream_iterator;
using std::ostringstream;
using std::runtime_error;
using std::string;
using std::vector;
using std::wstring;

namespace
{ // private

const string SSHD_PRIVATE_KEY_FILE = "fixture_dsakey";
const string SSHD_PUBLIC_KEY_FILE = "fixture_dsakey.pub";
const string SSHD_WRONG_PRIVATE_KEY_FILE = "fixture_wrong_dsakey";
const string SSHD_WRONG_PUBLIC_KEY_FILE = "fixture_wrong_dsakey.pub";

template <typename ArgSequence>
string error_message_from_stderr(const string& command,
                                 const ArgSequence& arguments, child& process)
{
    pistream command_stderr(process.get_handle(stderr_id));

    ostringstream message;
    message << quoted(command) << " ";
    copy(arguments.begin(), arguments.end(),
         ostream_iterator<string>(message, " "));
    message << " failed: ";
    message << command_stderr.rdbuf() << std::flush;

    return message.str();
}

template <typename Out, typename ArgSequence>
Out single_value_from_executable(const path& executable,
                                 const ArgSequence& arguments)
{
    context ctx;
    ctx.env = self::get_environment();
    ctx.streams[stdout_id] = pipe();
    ctx.streams[stderr_id] = pipe();

    child process = create_child(executable.string(), arguments, ctx);

    pistream command_stdout(process.get_handle(stdout_id));
    Out out;
    command_stdout >> out;

    int status = process.wait();
    // TODO: Check if process exited, with WIFEXITED - may need to upgrade
    // Boost.Process to the 2012 version to get mitigate.hpp, which handles this
    if (status == 0)
    {
        return out;
    }
    else
    {
        BOOST_THROW_EXCEPTION(runtime_error(error_message_from_stderr(
            executable.string(), arguments, process)));
    }
}

template <typename Out, typename ArgSequence>
Out single_value_from_command(const string& command,
                              const ArgSequence& arguments)
{
    path command_executable = find_executable_in_path(command);

    return single_value_from_executable<Out>(command_executable, arguments);
}

template <typename Out, typename ArgSequence>
Out single_value_from_docker_command(const ArgSequence& arguments)
{
    return single_value_from_command<Out>("docker", arguments);
}

template <typename Out, typename ArgSequence>
Out single_value_from_docker_machine_command(const ArgSequence& arguments)
{
    return single_value_from_command<Out>("docker-machine", arguments);
}

template <typename ArgSequence>
void run_docker_command(const ArgSequence& arguments)
{
    single_value_from_docker_command<string>(arguments);
}

optional<string> docker_machine_name()
{
    const string docker_machine_name_variable = "DOCKER_MACHINE_NAME";

    boost::process::environment environment = self::get_environment();
    if (environment.count(docker_machine_name_variable) == 1)
    {
        return environment[docker_machine_name_variable];
    }
    else
    {
        return optional<string>();
    }
}
}

extern "C" {
extern void ERR_free_strings();
extern void ERR_remove_state(unsigned long);
extern void EVP_cleanup();
extern void CRYPTO_cleanup_all_ex_data();
extern void ENGINE_cleanup();
extern void CONF_modules_unload(int);
extern void CONF_modules_free();
extern void RAND_cleanup();
}

namespace
{

struct global_fixture
{
    global_fixture()
    {
        // Ensure the docker image has been built
        vector<string> build_command = (list_of(string("build")), "-t",
                                        "swish/openssh_server", "ssh_server");
        run_docker_command(build_command);
    }

    ~global_fixture()
    {
        // We call this here as a bit of a hack to stop memory-leak
        // detection incorrectly detecting OpenSSL global data as a
        // leak
        swish::connection::session_pool().destroy();
        ::RAND_cleanup();
        ::ENGINE_cleanup();
        ::CONF_modules_unload(1);
        ::CONF_modules_free();
        ::EVP_cleanup();
        ::ERR_free_strings();
        ::ERR_remove_state(0);
        ::CRYPTO_cleanup_all_ex_data();
    }
};
}

BOOST_GLOBAL_FIXTURE(global_fixture);

namespace test
{
namespace fixtures
{
openssh_fixture::openssh_fixture()
{
    vector<string> docker_command =
        (list_of(string("run")), "--detach", "-P", "swish/openssh_server");
    m_container_id = single_value_from_docker_command<string>(docker_command);
    m_host = ask_docker_for_host();
    m_port = ask_docker_for_port();
}

openssh_fixture::~openssh_fixture()
{

}

void openssh_fixture::stop_server()
{
    vector<string> stop_command = (list_of(string("stop")), m_container_id);
    run_docker_command(stop_command);
}

void openssh_fixture::restart_server()
{
    stop_server();

    vector<string> docker_command =
        (list_of(string("run")), "--detach", "-p",
         lexical_cast<string>(m_port) + ":22", "swish/openssh_server");
    m_container_id = single_value_from_docker_command<string>(docker_command);
    // We make sure we bind to the same port in the new docker container.  Do we
    // have to do anything to ensure we get the same IP?  Presumably not unless
    // docker-machine changed machine in the middle of restarting.
    assert(ask_docker_for_host() == m_host);
    assert(ask_docker_for_port() == m_port);
}

string openssh_fixture::host() const
{
    return m_host;
}

wstring openssh_fixture::whost() const
{
    generator gen;
    locale loc = gen(get_system_locale());
    return to_utf<wchar_t>(m_host, loc);
}

string openssh_fixture::ask_docker_for_host() const
{
    optional<string> active_docker_machine = docker_machine_name();
    if (active_docker_machine)
    {
        // This can be flaky when tests run in parallel (see
        // http://example.com), so we retry a few
        // times with exponential backoff if it fails
        int attempt_no = 0;
        boost::posix_time::milliseconds wait_time(100);
        while (true)
        {
            ++attempt_no;
            try
            {
                vector<string> machine_ip_command =
                    (list_of(string("ip")), "default");
                return single_value_from_docker_machine_command<string>(
                    machine_ip_command);
            }
            catch (const runtime_error&)
            {
                if (attempt_no > 5)
                {
                    throw;
                }
                else
                {
                    wait_time *= 2;
                }
            }
            boost::this_thread::sleep(wait_time);
        }
    }
    else
    {
        vector<string> inspect_host_command =
            (list_of(string("inspect")), "--format",
             "{{ .NetworkSettings.IPAddress }}", m_container_id);
        return single_value_from_docker_command<string>(inspect_host_command);
    }
}

string openssh_fixture::user() const
{
    return "swish";
}

wstring openssh_fixture::wuser() const
{
    return L"swish";
}

int openssh_fixture::port() const
{
    return m_port;
}

string openssh_fixture::password() const
{
    return "my test password";
}

wstring openssh_fixture::wpassword() const
{
    return L"my test password";
}

int openssh_fixture::ask_docker_for_port() const
{
    vector<string> inspect_host_command =
        (list_of(string("inspect")), "--format",
         "{{ index (index (index .NetworkSettings.Ports \"22/tcp\") "
         "0) \"HostPort\" }}",
         m_container_id);
    return single_value_from_docker_command<int>(inspect_host_command);
}

/**
 * The private half of a key-pair that is expected to authenticate successfully
 * with the fixture server.
 */
path openssh_fixture::private_key_path() const
{
    return SSHD_PRIVATE_KEY_FILE;
}

/**
 * The public half of a key-pair that is expected to authenticate successfully
 * with the fixture server.
 */
path openssh_fixture::public_key_path() const
{
    return SSHD_PUBLIC_KEY_FILE;
}

/**
 * The private half of a key-pair that is expected to fail to authenticate
 * with the fixture server.
 *
 * This must be in the same format as the successful key-pair so that the key
 * mismatches rather than format mismatches are the cause of authentication
 * failure regardless of which combination of keys is passed.
 */
path openssh_fixture::wrong_private_key_path() const
{
    return SSHD_WRONG_PRIVATE_KEY_FILE;
}

/**
 * The public half of a key-pair that is expected to fail to authenticate
 * with the fixture server.
 *
 * This must be in the same format as the successful key-pair so that the
 * key mismatches rather than format mismatches are the cause of
 * authentication
 * failure regardless of which combination of keys is passed.
 */
path openssh_fixture::wrong_public_key_path() const
{
    return SSHD_WRONG_PUBLIC_KEY_FILE;
}
}
} // namespace test::fixtures


try
	{
		refBuilder->add_from_file(ResourceHelper::get_ui_path("dialog_workspaces.glade"));
	}
	catch(const Glib::FileError& ex)
	{
		synfig::error("FileError: " + ex.what());
		return Glib::RefPtr<Gtk::Builder>();
	}
	catch(const Glib::MarkupError& ex)
	{
		synfig::error("MarkupError: " + ex.what());
		return Glib::RefPtr<Gtk::Builder>();
	}
	catch(const Gtk::BuilderError& ex)
	{
		synfig::error("BuilderError: " + ex.what());
		return Glib::RefPtr<Gtk::Builder>();
	}

/*!	\file gui/dialogs/dialog_workspaces.cpp
**	\brief Dialog for handling custom workspace list
**
**	\legal
**
**	This file is part of Synfig.
**
**	\endlegal
*/

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include <gui/dialogs/dialog_workspaces.h>

#include <gui/app.h>
#include <gui/localization.h>
#include <gui/resourcehelper.h>
#include <gui/workspacehandler.h>

#include <glibmm/fileutils.h>
#include <glibmm/markup.h>

#include <gtkmm/liststore.h>
#include <gtkmm/messagedialog.h>
#include <gtkmm/treeview.h>

#include <synfig/general.h>
#include <synfig/string_helper.h>

#endif

using namespace studio;

static Glib::RefPtr<Gtk::Builder> load_interface() {
	auto refBuilder = Gtk::Builder::create();

	return refBuilder;
}

class WorkspaceCols: public Gtk::TreeModel::ColumnRecord {
    public:
        WorkspaceCols() {
            this->add(this->col_name);
        }

        Gtk::TreeModelColumn<Glib::ustring> col_name;
};

Dialog_Workspaces::Dialog_Workspaces(Gtk::Dialog::BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refGlade) :
	Gtk::Dialog(cobject),
	builder(refGlade),
	rename_button(nullptr),
	delete_button(nullptr)
{
	Gtk::Button *button = nullptr;

	refGlade->get_widget("workspaces_close_button", button);
	if (button)
		button->signal_clicked().connect(sigc::mem_fun(*this, &Gtk::Dialog::close));

	refGlade->get_widget("workspaces_delete_button", delete_button);
	if (delete_button)
		delete_button->signal_clicked().connect(sigc::mem_fun(*this, &Dialog_Workspaces::on_delete_clicked));

	refGlade->get_widget("workspaces_rename_button", rename_button);
	if (rename_button)
		rename_button->signal_clicked().connect(sigc::mem_fun(*this, &Dialog_Workspaces::on_rename_clicked));

	Gtk::TreeView * workspace_treeview = nullptr;
	refGlade->get_widget("workspaces_treeview", workspace_treeview);
	if (workspace_treeview) {
		current_selection = workspace_treeview->get_selection();
		current_selection->signal_changed().connect(sigc::mem_fun(*this, &Dialog_Workspaces::on_selection_changed));

		workspace_model = Glib::RefPtr<Gtk::ListStore>::cast_dynamic(
					refGlade->get_object("workspaces_liststore")
				);
//		WorkspaceCols ws_cols;

		App::signal_custom_workspaces_changed().connect(sigc::mem_fun(*this, &Dialog_Workspaces::rebuild_list));

		rebuild_list();
	}
}

Dialog_Workspaces* Dialog_Workspaces::create(Gtk::Window& parent)
{
	auto refBuilder = load_interface();
	if (!refBuilder)
		return nullptr;
	Dialog_Workspaces * dialog = nullptr;
	refBuilder->get_widget_derived("dialog_workspaces", dialog);
	if (dialog) {
		dialog->set_transient_for(parent);
	}
	return dialog;
}

Dialog_Workspaces::~Dialog_Workspaces()
{

}

void Dialog_Workspaces::on_selection_changed()
{
	int count = current_selection->count_selected_rows();
	rename_button->set_sensitive(count == 1);
	delete_button->set_sensitive(count > 0);
}

void Dialog_Workspaces::on_delete_clicked()
{
	char msg[256];
	snprintf(msg, 255, _("Are you sure you want to delete %d workspaces?"), current_selection->count_selected_rows());
	Gtk::MessageDialog confirm_dlg(*this, msg, false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO, true);
	if (confirm_dlg.run() != Gtk::RESPONSE_YES)
		return;

	// get_selected_rows() return TreePath not TreeIter
	// So, deleting an item, invalidates the other TreePaths (they point to wrong items)
	std::vector<std::string> names;
	for (auto selected_path : current_selection->get_selected_rows()) {
		std::string name;
		workspace_model->get_iter(selected_path)->get_value(0, name);
		names.push_back(name);
	}
	for (const std::string & name : names) {
		App::get_workspace_handler()->remove_workspace(name);
	}
}

void Dialog_Workspaces::on_rename_clicked()
{
	std::string old_name;
	auto selected_path = current_selection->get_selected_rows()[0];
	workspace_model->get_iter(selected_path)->get_value(0, old_name);

	Gtk::MessageDialog dialog(*this, _("Type a name for this custom workspace:"), false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_NONE, true);
	dialog.add_button(_("Cancel"), Gtk::RESPONSE_CANCEL);
	Gtk::Button * ok_button = dialog.add_button(_("Ok"), Gtk::RESPONSE_OK);
	ok_button->set_sensitive(false);

	Gtk::Entry * name_entry = Gtk::manage(new Gtk::Entry());
	name_entry->set_margin_start(16);
	name_entry->set_margin_end(16);
	name_entry->signal_changed().connect([&](){
		std::string name = name_entry->get_text();
		synfig::trim(name);
		bool has_equal_sign = name.find("=") != std::string::npos;
		ok_button->set_sensitive(!name.empty() && !has_equal_sign);
		if (ok_button->is_sensitive())
			ok_button->grab_default();
	});
	name_entry->signal_activate().connect(sigc::mem_fun(*ok_button, &Gtk::Button::clicked));
	name_entry->set_text(old_name);

	dialog.get_content_area()->set_spacing(12);
	dialog.get_content_area()->add(*name_entry);

	ok_button->set_can_default(true);

	dialog.show_all();

	int response = dialog.run();
	if (response != Gtk::RESPONSE_OK)
		return;

	std::string name = name_entry->get_text();
	synfig::trim(name);

	if (old_name == name)
		return;

	if (App::get_workspace_handler()->has_workspace(name)) {
		Gtk::MessageDialog error_dlg(dialog, _("There is already a workspace with this name."), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
		error_dlg.run();
		return;
	}

	std::string tpl;
	App::get_workspace_handler()->get_workspace(old_name, tpl);
	App::get_workspace_handler()->remove_workspace(old_name);
	App::get_workspace_handler()->add_workspace(name, tpl);
}

void Dialog_Workspaces::rebuild_list()
{
	workspace_model->clear();

	WorkspaceHandler *workspaces = App::get_workspace_handler();
	std::vector<std::string> names;
	workspaces->get_name_list(names);
	for (const std::string & name : names)
		workspace_model->append()->set_value(0, name);
}


try {
    py::checkPyError(0);
  } catch (const nupic::Exception &e) {
    NTA_DEBUG << e.getMessage();
  }

/* ---------------------------------------------------------------------
 * Numenta Platform for Intelligent Computing (NuPIC)
 * with Numenta, Inc., for a separate license for this software code, the
 * following terms and conditions apply:
 *
 *
 * http://example.com
 * ---------------------------------------------------------------------
 */

/** @file
 * Implementation of PyHelpers test
 */

#include <gtest/gtest.h>
#include <limits>
#include <nupic/py_support/PyHelpers.hpp>

using namespace nupic;

class PyHelpersTest : public ::testing::Test {
public:
  PyHelpersTest() {
    NTA_DEBUG << "Py_Initialize()";
    Py_Initialize();
  }

  ~PyHelpersTest() {
    NTA_DEBUG << "Py_Finalize()";
    Py_Finalize();
  }
};

TEST_F(PyHelpersTest, pyPtrConstructionNULL) {
  PyObject *p = NULL;
  EXPECT_THROW(py::Ptr(p, /* allowNULL: */ false), std::exception);

  py::Ptr pp1(p, /* allowNULL: */ true);
  ASSERT_TRUE((PyObject *)pp1 == NULL);
  ASSERT_TRUE(pp1.isNULL());
}

TEST_F(PyHelpersTest, pyPtrConstructionNonNULL) {
  PyObject *p = PyTuple_New(1);
  py::Ptr pp2(p);
  ASSERT_TRUE(!pp2.isNULL());
  ASSERT_TRUE((PyObject *)pp2 == p);
  pp2.release();
  ASSERT_TRUE(pp2.isNULL());
  Py_DECREF(p);
}

TEST_F(PyHelpersTest, pyPtrConstructionAssign) {
  PyObject *p = PyTuple_New(1);
  ASSERT_TRUE(p->ob_refcnt == 1);
  py::Ptr pp(NULL, /* allowNULL */ true);
  ASSERT_TRUE(pp.isNULL());
  NTA_DEBUG << "*** Before assign";
  pp.assign(p);
  NTA_DEBUG << "*** After assign";
  ASSERT_TRUE(p->ob_refcnt == 2);
  ASSERT_TRUE(!(pp.isNULL()));
  Py_DECREF(p);
  ASSERT_TRUE(p->ob_refcnt == 1);
}

TEST_F(PyHelpersTest, pyString) {
  py::String ps1(std::string("123"));
  ASSERT_TRUE(PyString_Check(ps1) != 0);

  py::String ps2("123", size_t(3));
  ASSERT_TRUE(PyString_Check(ps2) != 0);

  py::String ps3("123");
  ASSERT_TRUE(PyString_Check(ps3) != 0);

  std::string s1(PyString_AsString(ps1));
  std::string s2(PyString_AsString(ps2));
  std::string s3(PyString_AsString(ps3));
  std::string expected("123");
  ASSERT_TRUE(s1 == expected);
  ASSERT_TRUE(s2 == expected);
  ASSERT_TRUE(s3 == expected);

  ASSERT_TRUE(std::string(ps1) == expected);
  ASSERT_TRUE(std::string(ps2) == expected);
  ASSERT_TRUE(std::string(ps3) == expected);

  PyObject *p = PyString_FromString("777");
  py::String ps4(p);
  ASSERT_TRUE(std::string(ps4) == std::string("777"));
}

TEST_F(PyHelpersTest, pyInt) {
  py::Int n1(-5);
  py::Int n2(-6666);
  py::Int n3(long(0));
  py::Int n4(555);
  py::Int n5(6666);

  ASSERT_TRUE(n1 == -5);
  int x = n2;
  int expected = -6666;
  ASSERT_TRUE(x == expected);
  ASSERT_TRUE(n3 == 0);
  ASSERT_TRUE(n4 == 555);
  x = n5;
  expected = 6666;
  ASSERT_TRUE(x == expected);
}

TEST_F(PyHelpersTest, pyLong) {
  py::Long n1(-5);
  py::Long n2(-66666666);
  py::Long n3(long(0));
  py::Long n4(555);
  py::Long n5(66666666);

  ASSERT_TRUE(n1 == -5);
  long x = n2;
  long expected = -66666666;
  ASSERT_TRUE(x == expected);
  ASSERT_TRUE(n3 == 0);
  ASSERT_TRUE(n4 == 555);
  x = n5;
  expected = 66666666;
  ASSERT_TRUE(x == expected);
}

TEST_F(PyHelpersTest, pyUnsignedLong) {
  py::UnsignedLong n1((unsigned long)(-5));
  py::UnsignedLong n2((unsigned long)(-66666666));
  py::UnsignedLong n3((unsigned long)(0));
  py::UnsignedLong n4(555);
  py::UnsignedLong n5(66666666);

  ASSERT_TRUE(n1 == (unsigned long)(-5));
  ASSERT_TRUE(n2 == (unsigned long)(-66666666));
  ASSERT_TRUE(n3 == 0);
  ASSERT_TRUE(n4 == 555);
  ASSERT_TRUE(n5 == 66666666);
}

TEST_F(PyHelpersTest, pyFloat) {
  ASSERT_TRUE(py::Float::getMax() == std::numeric_limits<double>::max());
  ASSERT_TRUE(py::Float::getMin() == std::numeric_limits<double>::min());

  py::Float max(std::numeric_limits<double>::max());
  py::Float min(std::numeric_limits<double>::min());
  py::Float n1(-0.5);
  py::Float n2(double(0));
  py::Float n3(333.555);
  py::Float n4(0.02);
  py::Float n5("0.02");

  ASSERT_TRUE(max == py::Float::getMax());
  ASSERT_TRUE(min == py::Float::getMin());
  ASSERT_TRUE(n1 == -0.5);
  ASSERT_TRUE(n2 == 0);
  ASSERT_TRUE(n3 == 333.555);
  ASSERT_TRUE(n4 == 0.02);
  ASSERT_TRUE(n5 == 0.02);
}

TEST_F(PyHelpersTest, pyBool) {
  const auto trueRefcount = Py_REFCNT(Py_True);
  const auto falseRefcount = Py_REFCNT(Py_False);

  // Construct from true.
  {
    py::Bool t(true);
    ASSERT_TRUE((bool)t);
  }

  // Verify refcounts were preserved.
  ASSERT_EQ(trueRefcount, Py_REFCNT(Py_True));
  ASSERT_EQ(falseRefcount, Py_REFCNT(Py_False));

  // Construct from false.
  {
    py::Bool f(false);
    ASSERT_FALSE((bool)f);
  }

  // Verify refcounts were preserved.
  ASSERT_EQ(trueRefcount, Py_REFCNT(Py_True));
  ASSERT_EQ(falseRefcount, Py_REFCNT(Py_False));

  // Construct from an existing PyObject.
  {
    Py_XINCREF(Py_True);
    py::Bool t(Py_True);
    ASSERT_TRUE((bool)t);
  }

  // Verify refcounts were preserved.
  ASSERT_EQ(trueRefcount, Py_REFCNT(Py_True));
  ASSERT_EQ(falseRefcount, Py_REFCNT(Py_False));
}

TEST_F(PyHelpersTest, pyTupleEmpty) {
  py::String s1("item_1");
  py::String s2("item_2");

  py::Tuple empty;
  ASSERT_TRUE(PyTuple_Check(empty) != 0);
  ASSERT_TRUE(empty.getCount() == 0);

  EXPECT_THROW(empty.setItem(0, s1), std::exception);
  EXPECT_THROW(empty.getItem(0), std::exception);
}

TEST_F(PyHelpersTest, pyTupleOneItem) {
  py::String s1("item_1");
  py::String s2("item_2");

  py::Tuple t1(1);
  ASSERT_TRUE(PyTuple_Check(t1) != 0);
  ASSERT_TRUE(t1.getCount() == 1);

  t1.setItem(0, s1);
  py::String item1(t1.getItem(0));
  ASSERT_TRUE(std::string(item1) == std::string(s1));

  py::String fastItem1(t1.fastGetItem(0));
  ASSERT_TRUE(std::string(fastItem1) == std::string(s1));
  fastItem1.release();

  EXPECT_THROW(t1.setItem(1, s2), std::exception);
  EXPECT_THROW(t1.getItem(1), std::exception);

  ASSERT_TRUE(t1.getCount() == 1);
}

TEST_F(PyHelpersTest, pyTupleTwoItems) {
  py::String s1("item_1");
  py::String s2("item_2");

  py::Tuple t2(2);
  ASSERT_TRUE(PyTuple_Check(t2) != 0);
  ASSERT_TRUE(t2.getCount() == 2);

  t2.setItem(0, s1);
  py::String item1(t2.getItem(0));
  ASSERT_TRUE(std::string(item1) == std::string(s1));
  py::String fastItem1(t2.fastGetItem(0));
  ASSERT_TRUE(std::string(fastItem1) == std::string(s1));
  fastItem1.release();

  t2.setItem(1, s2);
  py::String item2(t2.getItem(1));
  ASSERT_TRUE(std::string(item2) == std::string(s2));
  py::String fastItem2(t2.fastGetItem(1));
  ASSERT_TRUE(std::string(fastItem2) == std::string(s2));
  fastItem2.release();

  EXPECT_THROW(t2.setItem(2, s2), std::exception);
  EXPECT_THROW(t2.getItem(2), std::exception);

  ASSERT_TRUE(t2.getCount() == 2);
}

TEST_F(PyHelpersTest, pyListEmpty) {
  py::String s1("item_1");
  py::String s2("item_2");

  py::List empty;
  ASSERT_TRUE(PyList_Check(empty) != 0);
  ASSERT_TRUE(empty.getCount() == 0);

  EXPECT_THROW(empty.setItem(0, s1), std::exception);
  EXPECT_THROW(empty.getItem(0), std::exception);
}

TEST_F(PyHelpersTest, pyListOneItem) {
  py::String s1("item_1");
  py::String s2("item_2");

  py::List t1;
  ASSERT_TRUE(PyList_Check(t1) != 0);
  ASSERT_TRUE(t1.getCount() == 0);

  t1.append(s1);
  py::String item1(t1.getItem(0));
  ASSERT_TRUE(std::string(item1) == std::string(s1));
  py::String fastItem1(t1.fastGetItem(0));
  ASSERT_TRUE(std::string(fastItem1) == std::string(s1));
  fastItem1.release();

  ASSERT_TRUE(t1.getCount() == 1);
  ASSERT_TRUE(std::string(item1) == std::string(s1));

  EXPECT_THROW(t1.getItem(1), std::exception);
}

TEST_F(PyHelpersTest, pyListTwoItems) {
  py::String s1("item_1");
  py::String s2("item_2");

  py::List t2;
  ASSERT_TRUE(PyList_Check(t2) != 0);
  ASSERT_TRUE(t2.getCount() == 0);

  t2.append(s1);
  py::String item1(t2.getItem(0));
  ASSERT_TRUE(std::string(item1) == std::string(s1));
  py::String fastItem1(t2.fastGetItem(0));
  ASSERT_TRUE(std::string(fastItem1) == std::string(s1));
  fastItem1.release();

  t2.append(s2);
  ASSERT_TRUE(t2.getCount() == 2);

  py::String item2(t2.getItem(1));
  ASSERT_TRUE(std::string(item2) == std::string(s2));
  py::String fastItem2(t2.fastGetItem(1));
  ASSERT_TRUE(std::string(fastItem2) == std::string(s2));
  fastItem2.release();

  EXPECT_THROW(t2.getItem(2), std::exception);
}

TEST_F(PyHelpersTest, pyDictEmpty) {
  py::Dict d;
  ASSERT_EQ(PyDict_Size(d), 0);

  ASSERT_TRUE(d.getItem("blah") == NULL);
}

TEST_F(PyHelpersTest, pyDictExternalPyObjectFailed) {
  // NULL object
  EXPECT_THROW(py::Dict(NULL), std::exception);

  // Wrong type (must be a dictionary)
  py::String s("1234");
  try {
    py::Dict d(s.release());
    NTA_THROW << "py::Dict d(s) Should fail!!!";
  } catch (...) {
  }
  // SHOULDFAIL fails to fail :-)
}

TEST_F(PyHelpersTest, pyDictExternalPyObjectSuccessful) {
  PyObject *p = PyDict_New();
  PyDict_SetItem(p, py::String("1234"), py::String("5678"));

  py::Dict d(p);

  ASSERT_TRUE(PyDict_Contains(d, py::String("1234")) == 1);

  PyDict_SetItem(d, py::String("777"), py::String("999"));

  ASSERT_TRUE(PyDict_Contains(d, py::String("777")) == 1);
}

// getItem with default (exisiting and non-exisitng key)
TEST_F(PyHelpersTest, pyDictGetItem) {
  py::Dict d;
  d.setItem("A", py::String("AAA"));

  PyObject *defaultItem = (PyObject *)123;

  py::String A(d.getItem("A"));
  ASSERT_TRUE(std::string(A) == std::string("AAA"));

  // No "B" in the dict, so expect to get the default item
  PyObject *B = (d.getItem("B", defaultItem));
  ASSERT_TRUE(B == defaultItem);

  PyDict_SetItem(d, py::String("777"), py::String("999"));
  ASSERT_TRUE(PyDict_Contains(d, py::String("777")) == 1);
}

TEST_F(PyHelpersTest, pyModule) {
  py::Module module("sys");
  ASSERT_TRUE(std::string(PyModule_GetName(module)) == std::string("sys"));
}

TEST_F(PyHelpersTest, pyClass) { py::Class c("datetime", "date"); }

TEST_F(PyHelpersTest, pyInstance) {

  py::Tuple args(3);
  args.setItem(0, py::Long(2000));
  args.setItem(1, py::Long(11));
  args.setItem(2, py::Long(5));
  py::Instance date("datetime", "date", args, py::Dict());

  {
    py::String result(date.invoke("__str__", py::Tuple(), py::Dict()));
    std::string res((const char *)result);
    std::string expected("2000-11-05");
    ASSERT_TRUE(res == expected);
  }

  {
    py::String result(date.invoke("__str__", py::Tuple(), py::Dict()));
    std::string res((const char *)result);
    std::string expected("2000-11-05");
    ASSERT_TRUE(!(date.hasAttr("No such attribute")));
    ASSERT_TRUE(date.hasAttr("year"));
  }

  {
    py::Int year(date.getAttr("year"));
    ASSERT_TRUE(2000 == long(year));
  }

  {
    std::string res((const char *)py::String(date.toString()));
    std::string expected("2000-11-05");
    ASSERT_TRUE(res == expected);
  }
}

TEST_F(PyHelpersTest, pyCustomException) {
  py::Tuple args(1);
  args.setItem(0, py::String("error message!"));
  py::Instance e(PyExc_RuntimeError, args);
  e.setAttr("traceback", py::String("traceback!!!"));

  PyErr_SetObject(PyExc_RuntimeError, e);


try
    {
        baker->setOutputStream(&log);
        baker->bakeAllMaterials(doc, imageSearchPath, outputFileName);
    }
    catch (mx::Exception& e)
    {
        const mx::FilePath& sourceUri = doc->getSourceUri();
        log << sourceUri.asString() + " failed baking process: " + e.what() << std::endl;
    }

#include <MaterialXTest/Catch/catch.hpp>
#include <MaterialXTest/MaterialXRender/RenderUtil.h>

#include <MaterialXRenderGlsl/TextureBaker.h>

#include <MaterialXRender/GeometryHandler.h>
#include <MaterialXRender/StbImageLoader.h>
#if defined(MATERIALX_BUILD_OIIO)
#include <MaterialXRender/OiioImageLoader.h>
#endif

#include <MaterialXFormat/Util.h>

namespace mx = MaterialX;

// Render validation tester for the GLSL shading language
class GlslShaderRenderTester : public RenderUtil::ShaderRenderTester
{
  public:
    explicit GlslShaderRenderTester(mx::ShaderGeneratorPtr shaderGenerator) :
        RenderUtil::ShaderRenderTester(shaderGenerator)
    {
    }

  protected:
    void loadAdditionalLibraries(mx::DocumentPtr document,
                                 GenShaderUtil::TestSuiteOptions& options) override;

    void registerLights(mx::DocumentPtr document, const GenShaderUtil::TestSuiteOptions &options,
                        mx::GenContext& context) override;

    void createRenderer(std::ostream& log) override;

    void transformUVs(const mx::MeshList& meshes, const mx::Matrix44& matrixTransform) const;

    bool runRenderer(const std::string& shaderName,
                     mx::TypedElementPtr element,
                     mx::GenContext& context,
                     mx::DocumentPtr doc,
                     std::ostream& log,
                     const GenShaderUtil::TestSuiteOptions& testOptions,
                     RenderUtil::RenderProfileTimes& profileTimes,
                     const mx::FileSearchPath& imageSearchPath,
                     const std::string& outputPath = ".",
                     mx::ImageVec* imageVec = nullptr) override;

    bool saveImage(const mx::FilePath& filePath, mx::ConstImagePtr image, bool verticalFlip) const override;

    bool canBake() const override
    {
        return true;
    }

    void runBake(mx::DocumentPtr doc, const mx::FileSearchPath& imageSearchPath, const mx::FilePath& outputFilename,
                 unsigned int bakeWidth, unsigned int bakeHeight, bool bakeHdr, std::ostream& log) override;

    mx::GlslRendererPtr _renderer;
    mx::LightHandlerPtr _lightHandler;
};

// In addition to standard texture and shader definition libraries, additional lighting files
// are loaded in. If no files are specifed in the input options, a sample
// compound light type and a set of lights in a "light rig" are loaded in to a given
void GlslShaderRenderTester::loadAdditionalLibraries(mx::DocumentPtr document,
                                                     GenShaderUtil::TestSuiteOptions& options)
{
    mx::FilePath lightDir = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Materials/TestSuite/lights");
    for (const auto& lightFile : options.lightFiles)
    {
        loadLibrary(lightDir / mx::FilePath(lightFile), document);
    }
}

// Create a light handler and populate it based on lights found in a given document
void GlslShaderRenderTester::registerLights(mx::DocumentPtr document,
                                            const GenShaderUtil::TestSuiteOptions &options,
                                            mx::GenContext& context)
{
    _lightHandler = mx::LightHandler::create();

    // Scan for lights
    std::vector<mx::NodePtr> lights;
    _lightHandler->findLights(document, lights);
    _lightHandler->registerLights(document, lights, context);

    // Set the list of lights on the with the generator
    _lightHandler->setLightSources(lights);

    // Load environment lights.
    mx::ImagePtr envRadiance = _renderer->getImageHandler()->acquireImage(options.radianceIBLPath);
    mx::ImagePtr envIrradiance = _renderer->getImageHandler()->acquireImage(options.irradianceIBLPath);
    REQUIRE(envRadiance);
    REQUIRE(envIrradiance);
    _lightHandler->setEnvRadianceMap(envRadiance);
    _lightHandler->setEnvIrradianceMap(envIrradiance);
}

// Create a renderer with the apporpraite image, geometry and light handlers.
// The light handler on the renderer is cleared on initialization to indicate no lighting
// is required. During code generation, if the element to validate requires lighting then
// the handler _lightHandler will be used.
void GlslShaderRenderTester::createRenderer(std::ostream& log)
{
    bool initialized = false;
    try
    {
        _renderer = mx::GlslRenderer::create();
        _renderer->initialize();

        // Set image handler on renderer
        mx::StbImageLoaderPtr stbLoader = mx::StbImageLoader::create();
        mx::ImageHandlerPtr imageHandler = mx::GLTextureHandler::create(stbLoader);
#if defined(MATERIALX_BUILD_OIIO)
        mx::OiioImageLoaderPtr oiioLoader = mx::OiioImageLoader::create();
        imageHandler->addLoader(oiioLoader);
#endif
        _renderer->setImageHandler(imageHandler);

        // Set light handler.
        _renderer->setLightHandler(nullptr);

        initialized = true;
    }
    catch (mx::ExceptionRenderError& e)
    {
        for (const auto& error : e.errorLog())
        {
            log << e.what() << " " << error << std::endl;
        }
    }
    catch (mx::Exception& e)
    {
        log << e.what() << std::endl;
    }
    REQUIRE(initialized);
}

bool GlslShaderRenderTester::saveImage(const mx::FilePath& filePath, mx::ConstImagePtr image, bool verticalFlip) const
{
    return _renderer->getImageHandler()->saveImage(filePath, image, verticalFlip);
}

// If these streams don't exist add them for testing purposes
void addAdditionalTestStreams(mx::MeshPtr mesh)
{
    size_t vertexCount = mesh->getVertexCount();
    if (vertexCount < 1)
    {
        return;
    }

    const std::string TEXCOORD_STREAM0_NAME("i_" + mx::MeshStream::TEXCOORD_ATTRIBUTE + "_0");
    mx::MeshStreamPtr texCoordStream1 = mesh->getStream(TEXCOORD_STREAM0_NAME);
    mx::MeshFloatBuffer uv = texCoordStream1->getData();

    const std::string TEXCOORD_STREAM1_NAME("i_" + mx::MeshStream::TEXCOORD_ATTRIBUTE + "_1");
    mx::MeshFloatBuffer* texCoordData2 = nullptr;
    if (!mesh->getStream(TEXCOORD_STREAM1_NAME))
    {
        mx::MeshStreamPtr texCoordStream2 = mx::MeshStream::create(TEXCOORD_STREAM1_NAME, mx::MeshStream::TEXCOORD_ATTRIBUTE, 1);
        texCoordStream2->setStride(2);
        texCoordData2 = &(texCoordStream2->getData());
        texCoordData2->resize(vertexCount * 2);
        mesh->addStream(texCoordStream2);
    }

    const std::string COLOR_STREAM0_NAME("i_" + mx::MeshStream::COLOR_ATTRIBUTE + "_0");
    mx::MeshFloatBuffer* colorData1 = nullptr;
    if (!mesh->getStream(COLOR_STREAM0_NAME))
    {
        mx::MeshStreamPtr colorStream1 = mx::MeshStream::create(COLOR_STREAM0_NAME, mx::MeshStream::COLOR_ATTRIBUTE, 0);
        colorData1 = &(colorStream1->getData());
        colorStream1->setStride(4);
        colorData1->resize(vertexCount * 4);
        mesh->addStream(colorStream1);
    }

    const std::string COLOR_STREAM1_NAME("i_" + mx::MeshStream::COLOR_ATTRIBUTE + "_1");
    mx::MeshFloatBuffer* colorData2 = nullptr;
    if (!mesh->getStream(COLOR_STREAM1_NAME))
    {
        mx::MeshStreamPtr colorStream2 = mx::MeshStream::create(COLOR_STREAM1_NAME, mx::MeshStream::COLOR_ATTRIBUTE, 1);
        colorData2 = &(colorStream2->getData());
        colorStream2->setStride(4);
        colorData2->resize(vertexCount * 4);
        mesh->addStream(colorStream2);
    }

    const std::string GEOM_INT_STREAM_NAME("i_" + mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE + "_geompropvalue_integer");
    int32_t* geomIntData = nullptr;
    if (!mesh->getStream(GEOM_INT_STREAM_NAME))
    {
        mx::MeshStreamPtr geomIntStream = mx::MeshStream::create(GEOM_INT_STREAM_NAME, mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE, 0);
        geomIntStream->setStride(1);
        geomIntStream->getData().resize(vertexCount);
        mesh->addStream(geomIntStream);
        // Float and int32 have same size.
        geomIntData = reinterpret_cast<int32_t*>(geomIntStream->getData().data());
    }

    const std::string GEOM_FLOAT_STREAM_NAME("i_" + mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE + "_geompropvalue_float");
    mx::MeshFloatBuffer* geomFloatData = nullptr;
    if (!mesh->getStream(GEOM_FLOAT_STREAM_NAME))
    {
        mx::MeshStreamPtr geomFloatStream = mx::MeshStream::create(GEOM_FLOAT_STREAM_NAME, mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE, 1);
        geomFloatData = &(geomFloatStream->getData());
        geomFloatStream->setStride(1);
        geomFloatData->resize(vertexCount);
        mesh->addStream(geomFloatStream);
    }

    const std::string GEOM_VECTOR2_STREAM_NAME("i_" + mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE + "_geompropvalue_vector2");
    mx::MeshFloatBuffer* geomVector2Data = nullptr;
    if (!mesh->getStream(GEOM_VECTOR2_STREAM_NAME))
    {
        mx::MeshStreamPtr geomVector2Stream = mx::MeshStream::create(GEOM_VECTOR2_STREAM_NAME, mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE, 1);
        geomVector2Data = &(geomVector2Stream->getData());
        geomVector2Stream->setStride(2);
        geomVector2Data->resize(vertexCount * 2);
        mesh->addStream(geomVector2Stream);
    }

    const std::string GEOM_VECTOR3_STREAM_NAME("i_" + mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE + "_geompropvalue_vector3");
    mx::MeshFloatBuffer* geomVector3Data = nullptr;
    if (!mesh->getStream(GEOM_VECTOR3_STREAM_NAME))
    {
        mx::MeshStreamPtr geomVector3Stream = mx::MeshStream::create(GEOM_VECTOR3_STREAM_NAME, mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE, 1);
        geomVector3Data = &(geomVector3Stream->getData());
        geomVector3Stream->setStride(3);
        geomVector3Data->resize(vertexCount * 3);
        mesh->addStream(geomVector3Stream);
    }

    const std::string GEOM_VECTOR4_STREAM_NAME("i_" + mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE + "_geompropvalue_vector4");
    mx::MeshFloatBuffer* geomVector4Data = nullptr;
    if (!mesh->getStream(GEOM_VECTOR4_STREAM_NAME))
    {
        mx::MeshStreamPtr geomVector4Stream = mx::MeshStream::create(GEOM_VECTOR4_STREAM_NAME, mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE, 1);
        geomVector4Data = &(geomVector4Stream->getData());
        geomVector4Stream->setStride(4);
        geomVector4Data->resize(vertexCount * 4);
        mesh->addStream(geomVector4Stream);
    }

    const std::string GEOM_COLOR2_STREAM_NAME("i_" + mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE + "_geompropvalue_color2");
    mx::MeshFloatBuffer* geomColor2Data = nullptr;
    if (!mesh->getStream(GEOM_COLOR2_STREAM_NAME))
    {
        mx::MeshStreamPtr geomColor2Stream = mx::MeshStream::create(GEOM_COLOR2_STREAM_NAME, mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE, 1);
        geomColor2Data = &(geomColor2Stream->getData());
        geomColor2Stream->setStride(2);
        geomColor2Data->resize(vertexCount * 2);
        mesh->addStream(geomColor2Stream);
    }

    const std::string GEOM_COLOR3_STREAM_NAME("i_" + mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE + "_geompropvalue_color3");
    mx::MeshFloatBuffer* geomColor3Data = nullptr;
    if (!mesh->getStream(GEOM_COLOR3_STREAM_NAME))
    {
        mx::MeshStreamPtr geomColor3Stream = mx::MeshStream::create(GEOM_COLOR3_STREAM_NAME, mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE, 1);
        geomColor3Data = &(geomColor3Stream->getData());
        geomColor3Stream->setStride(3);
        geomColor3Data->resize(vertexCount * 3);
        mesh->addStream(geomColor3Stream);
    }

    const std::string GEOM_COLOR4_STREAM_NAME("i_" + mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE + "_geompropvalue_color4");
    mx::MeshFloatBuffer* geomColor4Data = nullptr;
    if (!mesh->getStream(GEOM_COLOR4_STREAM_NAME))
    {
        mx::MeshStreamPtr geomColor4Stream = mx::MeshStream::create(GEOM_COLOR4_STREAM_NAME, mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE, 1);
        geomColor4Data = &(geomColor4Stream->getData());
        geomColor4Stream->setStride(4);
        geomColor4Data->resize(vertexCount * 4);
        mesh->addStream(geomColor4Stream);
    }

    auto sineData = [](float uv, float freq){
        const float PI = std::acos(-1.0f);
        float angle = uv * 2 * PI * freq;
        return std::sin(angle) / 2.0f + 1.0f;
    };
    if (!uv.empty())
    {
        for (size_t i = 0; i < vertexCount; i++)
        {
            const size_t i2 = 2 * i;
            const size_t i21 = i2 + 1;
            const size_t i3 = 3 * i;
            const size_t i4 = 4 * i;

            // Fake second set of texture coordinates
            if (texCoordData2)
            {
                (*texCoordData2)[i2] = uv[i21];
                (*texCoordData2)[i21] = uv[i2];
            }
            if (colorData1)
            {
                // Fake some colors
                (*colorData1)[i4] = uv[i2];
                (*colorData1)[i4 + 1] = uv[i21];
                (*colorData1)[i4 + 2] = 1.0f;
                (*colorData1)[i4 + 3] = 1.0f;
            }
            if (colorData2)
            {
                (*colorData2)[i4] = 1.0f;
                (*colorData2)[i4 + 1] = uv[i2];
                (*colorData2)[i4 + 2] = uv[i21];
                (*colorData2)[i4 + 3] = 1.0f;
            }
            if (geomIntData)
            {
                geomIntData[i] = static_cast<int32_t>(uv[i21] * 5);
            }
            if (geomFloatData)
            {
                (*geomFloatData)[i] = sineData(uv[i21], 12.0f);
            }
            if (geomVector2Data)
            {
                (*geomVector2Data)[i2] = sineData(uv[i21], 6.0f);
                (*geomVector2Data)[i21] = 0.0f;
            }
            if (geomVector3Data)
            {
                (*geomVector3Data)[i3] = 0.0f;
                (*geomVector3Data)[i3 + 1] = sineData(uv[i21], 8.0f);
                (*geomVector3Data)[i3 + 2] = 0.0f;
            }
            if (geomVector4Data)
            {
                (*geomVector4Data)[i4] = 0.0f;
                (*geomVector4Data)[i4 + 1] = 0.0f;
                (*geomVector4Data)[i4 + 2] = sineData(uv[i21], 10.0f);
                (*geomVector4Data)[i4 + 3] = 1.0f;
            }

            if (geomColor2Data)
            {
                (*geomColor2Data)[i2] = sineData(uv[i2], 10.0f);
                (*geomColor2Data)[i21] = 0.0f;
            }
            if (geomColor3Data)
            {
                (*geomColor3Data)[i3] = 0.0f;
                (*geomColor3Data)[i3 + 1] = sineData(uv[i2], 8.0f);
                (*geomColor3Data)[i3 + 2] = 0.0f;
            }
            if (geomColor4Data)
            {
                (*geomColor4Data)[i4] = 0.0f;
                (*geomColor4Data)[i4 + 1] = 0.0f;
                (*geomColor4Data)[i4 + 2] = sineData(uv[i2], 6.0f);
                (*geomColor4Data)[i4 + 3] = 1.0f;
            }
        }
    }
}

void GlslShaderRenderTester::transformUVs(const mx::MeshList& meshes, const mx::Matrix44& matrixTransform) const
{
    for(mx::MeshPtr mesh : meshes)
    {
        // Transform texture coordinates.
        mx::MeshStreamPtr uvStream = mesh->getStream(mx::MeshStream::TEXCOORD_ATTRIBUTE, 0);
        uvStream->transform(matrixTransform);

        // Regenerate tangents
        mx::MeshStreamPtr tangentStream = mesh->getStream(mx::MeshStream::TANGENT_ATTRIBUTE, 0);
        if (tangentStream)
        {
            mesh->removeStream(tangentStream);
            tangentStream = mesh->generateTangents(mesh->getStream(mx::MeshStream::POSITION_ATTRIBUTE, 0),
                                                   mesh->getStream(mx::MeshStream::NORMAL_ATTRIBUTE, 0),
                                                   uvStream);
            if (tangentStream)
            {
                mesh->addStream(tangentStream);
            }
        }
    }
}

bool GlslShaderRenderTester::runRenderer(const std::string& shaderName,
                                          mx::TypedElementPtr element,
                                          mx::GenContext& context,
                                          mx::DocumentPtr doc,
                                          std::ostream& log,
                                          const GenShaderUtil::TestSuiteOptions& testOptions,
                                          RenderUtil::RenderProfileTimes& profileTimes,
                                          const mx::FileSearchPath& imageSearchPath,
                                          const std::string& outputPath,
                                          mx::ImageVec* imageVec)
{
    mx::ScopedTimer totalGLSLTime(&profileTimes.languageTimes.totalTime);

    const mx::ShaderGenerator& shadergen = context.getShaderGenerator();

    // Perform validation if requested
    if (testOptions.validateElementToRender)
    {
        std::string message;
        if (!element->validate(&message))
        {
            log << "Element is invalid: " << message << std::endl;
            return false;
        }
    }

    std::vector<mx::GenOptions> optionsList;
    getGenerationOptions(testOptions, context.getOptions(), optionsList);

    if (element && doc)
    {
        log << "------------ Run GLSL validation with element: " << element->getNamePath() << "-------------------" << std::endl;

        for (auto options : optionsList)
        {
            profileTimes.elementsTested++;

            mx::FilePath outputFilePath = outputPath;
            // Use separate directory for reduced output
            if (options.shaderInterfaceType == mx::SHADER_INTERFACE_REDUCED)
            {
                outputFilePath = outputFilePath / mx::FilePath("reduced");
            }

            // Note: mkdir will fail if the directory already exists which is ok.
            {
                mx::ScopedTimer ioDir(&profileTimes.languageTimes.ioTime);
                outputFilePath.createDirectory();
            }

            std::string shaderPath = mx::FilePath(outputFilePath) / mx::FilePath(shaderName);
            mx::ShaderPtr shader;
            try
            {
                mx::ScopedTimer transpTimer(&profileTimes.languageTimes.transparencyTime);
                options.hwTransparency = mx::isTransparentSurface(element, shadergen.getTarget());
                transpTimer.endTimer();

                mx::ScopedTimer generationTimer(&profileTimes.languageTimes.generationTime);
                mx::GenOptions& contextOptions = context.getOptions();
                contextOptions = options;
                contextOptions.targetColorSpaceOverride = "lin_rec709";
                contextOptions.hwSpecularEnvironmentMethod = testOptions.specularEnvironmentMethod;
                shader = shadergen.generate(shaderName, element, context);
                generationTimer.endTimer();
            }
            catch (mx::Exception& e)
            {
                log << ">> " << e.what() << "\n";
                shader = nullptr;
            }

            CHECK(shader != nullptr);
            if (shader == nullptr)
            {
                log << ">> Failed to generate shader\n";
                return false;
            }
            const std::string& vertexSourceCode = shader->getSourceCode(mx::Stage::VERTEX);
            const std::string& pixelSourceCode = shader->getSourceCode(mx::Stage::PIXEL);
            CHECK(vertexSourceCode.length() > 0);
            CHECK(pixelSourceCode.length() > 0);

            if (testOptions.dumpGeneratedCode)
            {
                mx::ScopedTimer dumpTimer(&profileTimes.languageTimes.ioTime);
                std::ofstream file;
                file.open(shaderPath + "_vs.glsl");
                file << vertexSourceCode;
                file.close();
                file.open(shaderPath + "_ps.glsl");
                file << pixelSourceCode;
                file.close();
            }

            if (!testOptions.compileCode)
            {
                return false;
            }

            // Validate
            MaterialX::GlslProgramPtr program = _renderer->getProgram();
            bool validated = false;
            try
            {
                mx::GeometryHandlerPtr geomHandler = _renderer->getGeometryHandler();

                bool isShader = mx::elementRequiresShading(element);
                if (isShader)
                {
                    // Set shaded element geometry
                    mx::FilePath geomPath;
                    if (!testOptions.shadedGeometry.isEmpty())
                    {
                        if (!testOptions.shadedGeometry.isAbsolute())
                        {
                            geomPath = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Geometry") / testOptions.shadedGeometry;
                        }
                        else
                        {
                            geomPath = testOptions.shadedGeometry;
                        }
                    }
                    else
                    {
                        geomPath = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Geometry/shaderball.obj");
                    }
                    if (!geomHandler->hasGeometry(geomPath))
                    {
                        geomHandler->clearGeometry();
                        geomHandler->loadGeometry(geomPath);
                        const mx::MeshList& meshes = geomHandler->getMeshes();
                        if (!meshes.empty())
                        {
                            addAdditionalTestStreams(meshes[0]);
                            transformUVs(meshes, testOptions.transformUVs);
                        }
                    }

                    // Set shaded element lights
                    _renderer->setLightHandler(_lightHandler);
                }
                else
                {
                    // Set unshaded element geometry
                    mx::FilePath geomPath;
                    if (!testOptions.unShadedGeometry.isEmpty())
                    {
                        if (!testOptions.unShadedGeometry.isAbsolute())
                        {
                            geomPath = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Geometry") / testOptions.unShadedGeometry;
                        }
                        else
                        {
                            geomPath = testOptions.unShadedGeometry;
                        }
                    }
                    else
                    {
                        geomPath = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Geometry/sphere.obj");
                    }
                    if (!geomHandler->hasGeometry(geomPath))
                    {
                        geomHandler->clearGeometry();
                        geomHandler->loadGeometry(geomPath);
                        const mx::MeshList& meshes = geomHandler->getMeshes();
                        if (!meshes.empty())
                        {
                            addAdditionalTestStreams(meshes[0]);
                            transformUVs(meshes, testOptions.transformUVs);
                        }
                    }

                    // Clear lights for unshaded element
                    _renderer->setLightHandler(nullptr);
                }

                {
                    mx::ScopedTimer compileTimer(&profileTimes.languageTimes.compileTime);
                    _renderer->createProgram(shader);
                    _renderer->validateInputs();
                }

                if (testOptions.dumpUniformsAndAttributes)
                {
                    mx::ScopedTimer printTimer(&profileTimes.languageTimes.ioTime);
                    log << "* Uniform:" << std::endl;
                    program->printUniforms(log);
                    log << "* Attributes:" << std::endl;
                    program->printAttributes(log);

                    log << "* Uniform UI Properties:" << std::endl;
                    const std::string& target = shadergen.getTarget();
                    const MaterialX::GlslProgram::InputMap& uniforms = program->getUniformsList();
                    for (const auto& uniform : uniforms)
                    {
                        const std::string& path = uniform.second->path;
                        if (path.empty())
                        {
                            continue;
                        }

                        mx::UIProperties uiProperties;
                        mx::ElementPtr pathElement = doc->getDescendant(path);
                        mx::InputPtr input = pathElement ? pathElement->asA<mx::Input>() : nullptr;
                        if (getUIProperties(input, target, uiProperties) > 0)
                        {
                            log << "Program Uniform: " << uniform.first << ". Path: " << path;
                            if (!uiProperties.uiName.empty())
                                log << ". UI Name: \"" << uiProperties.uiName << "\"";
                            if (!uiProperties.uiFolder.empty())
                                log << ". UI Folder: \"" << uiProperties.uiFolder << "\"";
                            if (!uiProperties.enumeration.empty())
                            {
                                log << ". Enumeration: {";
                                for (size_t i = 0; i < uiProperties.enumeration.size(); i++)
                                    log << uiProperties.enumeration[i] << " ";
                                log << "}";
                            }
                            if (!uiProperties.enumerationValues.empty())
                            {
                                log << ". Enum Values: {";
                                for (size_t i = 0; i < uiProperties.enumerationValues.size(); i++)
                                    log << uiProperties.enumerationValues[i]->getValueString() << "; ";
                                log << "}";
                            }
                            if (uiProperties.uiMin)
                                log << ". UI Min: " << uiProperties.uiMin->getValueString();
                            if (uiProperties.uiMax)
                                log << ". UI Max: " << uiProperties.uiMax->getValueString();
                            if (uiProperties.uiSoftMin)
                                log << ". UI Soft Min: " << uiProperties.uiSoftMin->getValueString();
                            if (uiProperties.uiSoftMax)
                                log << ". UI Soft Max: " << uiProperties.uiSoftMax->getValueString();
                            if (uiProperties.uiStep)
                                log << ". UI Step: " << uiProperties.uiStep->getValueString();
                            log << std::endl;
                        }
                    }
                }

                if (testOptions.renderImages)
                {
                    {
                        mx::ScopedTimer renderTimer(&profileTimes.languageTimes.renderTime);
                        _renderer->getImageHandler()->setSearchPath(imageSearchPath);
                        _renderer->setSize(static_cast<unsigned int>(testOptions.renderSize[0]), static_cast<unsigned int>(testOptions.renderSize[1]));
                        _renderer->render();
                    }

                    if (testOptions.saveImages)
                    {
                        mx::ScopedTimer ioTimer(&profileTimes.languageTimes.imageSaveTime);
                        std::string fileName = shaderPath + "_glsl.png";
                        mx::ImagePtr image = _renderer->captureImage();
                        if (image)
                        {
                            _renderer->getImageHandler()->saveImage(fileName, image, true);
                            if (imageVec)
                            {
                                imageVec->push_back(image);
                            }
                        }
                    }
                }

                validated = true;
            }
            catch (mx::ExceptionRenderError& e)
            {
                // Always dump shader stages on error
                std::ofstream file;
                file.open(shaderPath + "_vs.glsl");
                file << shader->getSourceCode(mx::Stage::VERTEX);
                file.close();
                file.open(shaderPath + "_ps.glsl");
                file << shader->getSourceCode(mx::Stage::PIXEL);
                file.close();

                for (const auto& error : e.errorLog())
                {
                    log << e.what() << " " << error << std::endl;
                }
                log << ">> Refer to shader code in dump files: " << shaderPath << "_ps.glsl and _vs.glsl files" << std::endl;
                WARN(std::string(e.what()) + " in " + shaderPath);
            }
            catch (mx::Exception& e)
            {
                log << e.what() << std::endl;
                WARN(std::string(e.what()) + " in " + shaderPath);
            }
            CHECK(validated);
        }
    }
    return true;
}

void GlslShaderRenderTester::runBake(mx::DocumentPtr doc, const mx::FileSearchPath& imageSearchPath, const mx::FilePath& outputFileName,
                                      unsigned int bakeWidth, unsigned int bakeHeight, bool bakeHdr, std::ostream& log)
{
    mx::ImageVec imageVec = _renderer->getImageHandler()->getReferencedImages(doc);
    auto maxImageSize = mx::getMaxDimensions(imageVec);
    bakeWidth = std::max(bakeWidth, maxImageSize.first);
    bakeHeight = std::max(bakeHeight, maxImageSize.second);

    mx::Image::BaseType baseType = bakeHdr ? mx::Image::BaseType::FLOAT : mx::Image::BaseType::UINT8;
    mx::TextureBakerPtr baker = mx::TextureBaker::create(bakeWidth, bakeHeight, baseType);
    baker->setupUnitSystem(doc);
    baker->setImageHandler(_renderer->getImageHandler());
    baker->setOptimizeConstants(true);
    baker->setHashImageNames(true);

}

TEST_CASE("Render: GLSL TestSuite", "[renderglsl]")
{
    GlslShaderRenderTester renderTester(mx::GlslShaderGenerator::create());

    const mx::FilePath testRootPath = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Materials/TestSuite");
    const mx::FilePath testRootPath2 = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Materials/Examples/StandardSurface");
    const mx::FilePath testRootPath3 = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Materials/Examples/UsdPreviewSurface");
    mx::FilePathVec testRootPaths;
    testRootPaths.push_back(testRootPath);
    testRootPaths.push_back(testRootPath2);
    testRootPaths.push_back(testRootPath3);

    mx::FilePath optionsFilePath = testRootPath / mx::FilePath("_options.mtlx");

    renderTester.validate(testRootPaths, optionsFilePath);
}