From 7ce4524ac09af35c94100e7cf11ab24f3c4e02f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 25 Mar 2015 12:13:44 +0100 Subject: [PATCH 01/11] Test execution stats improvements --stats can have optional values: --stats=out prints all test timings to standard output, --stats= prints all test timings to a file in tab-separated format. Stats are now kept in vector to allow duplicated values. --- test/Stats.cpp | 33 +++++++++++++++++++++++++-------- test/Stats.h | 13 ++++++++++--- test/TestHelper.cpp | 19 ++++++++++++++++--- test/TestHelper.h | 4 +++- test/state.cpp | 5 +---- test/vm.cpp | 5 +---- 6 files changed, 56 insertions(+), 23 deletions(-) diff --git a/test/Stats.cpp b/test/Stats.cpp index fa615cb50..e76d1ee00 100644 --- a/test/Stats.cpp +++ b/test/Stats.cpp @@ -19,6 +19,7 @@ #include #include +#include namespace dev { @@ -31,6 +32,11 @@ Stats& Stats::get() return instance; } +void Stats::suiteStarted(std::string const& _name) +{ + m_currentSuite = _name; +} + void Stats::testStarted(std::string const& _name) { m_currentTest = _name; @@ -39,7 +45,7 @@ void Stats::testStarted(std::string const& _name) void Stats::testFinished() { - m_stats[clock::now() - m_tp] = std::move(m_currentTest); + m_stats.push_back({clock::now() - m_tp, m_currentSuite + "/" + m_currentTest}); } std::ostream& operator<<(std::ostream& out, Stats::clock::duration const& d) @@ -52,31 +58,42 @@ Stats::~Stats() if (m_stats.empty()) return; + std::sort(m_stats.begin(), m_stats.end(), [](Stats::Item const& a, Stats::Item const& b){ + return a.duration < b.duration; + }); + auto& out = std::cout; auto itr = m_stats.begin(); auto min = *itr; auto max = *m_stats.rbegin(); std::advance(itr, m_stats.size() / 2); auto med = *itr; - auto tot = std::accumulate(m_stats.begin(), m_stats.end(), clock::duration{}, [](clock::duration const& a, stats_t::value_type const& v) + auto tot = std::accumulate(m_stats.begin(), m_stats.end(), clock::duration{}, [](clock::duration const& a, Stats::Item const& v) { - return a + v.first; + return a + v.duration; }); out << "\nSTATS:\n\n" << std::setfill(' '); - if (Options::get().statsFull) + if (Options::get().statsOutFile == "out") { for (auto&& s: m_stats) - out << " " << std::setw(40) << std::left << s.second.substr(0, 40) << s.first << " \n"; + out << " " << std::setw(40) << std::left << s.name.substr(0, 40) << s.duration << " \n"; out << "\n"; } + else if (!Options::get().statsOutFile.empty()) + { + // Output stats to file + std::ofstream file{Options::get().statsOutFile}; + for (auto&& s: m_stats) + file << s.name << "\t" << std::chrono::duration_cast(s.duration).count() << "\n"; + } out << " tot: " << tot << "\n" << " avg: " << (tot / m_stats.size()) << "\n\n" - << " min: " << min.first << " (" << min.second << ")\n" - << " med: " << med.first << " (" << med.second << ")\n" - << " max: " << max.first << " (" << max.second << ")\n"; + << " min: " << min.duration << " (" << min.name << ")\n" + << " med: " << med.duration << " (" << med.name << ")\n" + << " max: " << max.duration << " (" << max.name << ")\n"; } } diff --git a/test/Stats.h b/test/Stats.h index 7692a2b30..9b40c5fce 100644 --- a/test/Stats.h +++ b/test/Stats.h @@ -18,7 +18,7 @@ #pragma once #include -#include +#include #include "TestHelper.h" @@ -31,19 +31,26 @@ class Stats: public Listener { public: using clock = std::chrono::high_resolution_clock; - using stats_t = std::map; + + struct Item + { + clock::duration duration; + std::string name; + }; static Stats& get(); ~Stats(); + void suiteStarted(std::string const& _name) override; void testStarted(std::string const& _name) override; void testFinished() override; private: clock::time_point m_tp; + std::string m_currentSuite; std::string m_currentTest; - stats_t m_stats; + std::vector m_stats; }; } diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index b29c5dc3a..b3e64f47b 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -29,6 +29,7 @@ #include #include #include +#include "Stats.h" using namespace std; using namespace dev::eth; @@ -447,6 +448,9 @@ void executeTests(const string& _name, const string& _testPathAppendix, std::fun string testPath = getTestPath(); testPath += _testPathAppendix; + if (Options::get().stats) + Listener::registerListener(Stats::get()); + if (Options::get().fillTests) { try @@ -478,6 +482,7 @@ void executeTests(const string& _name, const string& _testPathAppendix, std::fun string s = asString(dev::contents(testPath + "/" + _name + ".json")); BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of " + testPath + "/" + _name + ".json is empty. Have you cloned the 'tests' repo branch develop and set ETHEREUM_TEST_PATH to its path?"); json_spirit::read_string(s, v); + Listener::notifySuiteStarted(_name); doTests(v, false); } catch (Exception const& _e) @@ -551,10 +556,12 @@ Options::Options() vmtrace = true; else if (arg == "--filltests") fillTests = true; - else if (arg == "--stats") + else if (arg.compare(0, 7, "--stats") == 0) + { stats = true; - else if (arg == "--stats=full") - stats = statsFull = true; + if (arg.size() > 7) + statsOutFile = arg.substr(8); // skip '=' char + } else if (arg == "--performance") performance = true; else if (arg == "--quadratic") @@ -602,6 +609,12 @@ void Listener::registerListener(Listener& _listener) g_listener = &_listener; } +void Listener::notifySuiteStarted(std::string const& _name) +{ + if (g_listener) + g_listener->suiteStarted(_name); +} + void Listener::notifyTestStarted(std::string const& _name) { if (g_listener) diff --git a/test/TestHelper.h b/test/TestHelper.h index ade20f5e4..05e01792d 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -164,7 +164,7 @@ public: bool vmtrace = false; ///< Create EVM execution tracer // TODO: Link with log verbosity? bool fillTests = false; ///< Create JSON test files from execution results bool stats = false; ///< Execution time stats - bool statsFull = false; ///< Output full stats - execution times for every test + std::string statsOutFile; ///< Stats output file. "out" for standard output /// Test selection /// @{ @@ -191,10 +191,12 @@ class Listener public: virtual ~Listener() = default; + virtual void suiteStarted(std::string const&) {} virtual void testStarted(std::string const& _name) = 0; virtual void testFinished() = 0; static void registerListener(Listener& _listener); + static void notifySuiteStarted(std::string const& _name); static void notifyTestStarted(std::string const& _name); static void notifyTestFinished(); diff --git a/test/state.cpp b/test/state.cpp index 4ab59f7a1..8168693b1 100644 --- a/test/state.cpp +++ b/test/state.cpp @@ -31,7 +31,6 @@ #include #include #include "TestHelper.h" -#include "Stats.h" using namespace std; using namespace json_spirit; @@ -42,9 +41,6 @@ namespace dev { namespace test { void doStateTests(json_spirit::mValue& v, bool _fillin) { - if (Options::get().stats) - Listener::registerListener(Stats::get()); - for (auto& i: v.get_obj()) { std::cout << " " << i.first << "\n"; @@ -255,6 +251,7 @@ BOOST_AUTO_TEST_CASE(stRandom) string s = asString(dev::contents(path.string())); BOOST_REQUIRE_MESSAGE(s.length() > 0, "Content of " + path.string() + " is empty. Have you cloned the 'tests' repo branch develop and set ETHEREUM_TEST_PATH to its path?"); json_spirit::read_string(s, v); + test::Listener::notifySuiteStarted(path.filename().string()); dev::test::doStateTests(v, false); } catch (Exception const& _e) diff --git a/test/vm.cpp b/test/vm.cpp index 2bdafb270..cffbaa64d 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -25,7 +25,6 @@ #include #include #include "vm.h" -#include "Stats.h" using namespace std; using namespace json_spirit; @@ -311,9 +310,6 @@ namespace dev { namespace test { void doVMTests(json_spirit::mValue& v, bool _fillin) { - if (Options::get().stats) - Listener::registerListener(Stats::get()); - for (auto& i: v.get_obj()) { std::cout << " " << i.first << "\n"; @@ -549,6 +545,7 @@ BOOST_AUTO_TEST_CASE(vmRandom) string s = asString(dev::contents(path.string())); BOOST_REQUIRE_MESSAGE(s.length() > 0, "Content of " + path.string() + " is empty. Have you cloned the 'tests' repo branch develop and set ETHEREUM_TEST_PATH to its path?"); json_spirit::read_string(s, v); + test::Listener::notifySuiteStarted(path.filename().string()); doVMTests(v, false); } catch (Exception const& _e) From d1f9f0290faca526311a89dac007ed083ecf9bba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 25 Mar 2015 15:12:54 +0100 Subject: [PATCH 02/11] Add option to preload cache --- evmjit/libevmjit/Cache.cpp | 31 ++++++++++++++++++++++++++++ evmjit/libevmjit/Cache.h | 12 ++++++++++- evmjit/libevmjit/ExecutionEngine.cpp | 17 ++++++++++++--- 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/evmjit/libevmjit/Cache.cpp b/evmjit/libevmjit/Cache.cpp index d3cda1098..47a6386e9 100644 --- a/evmjit/libevmjit/Cache.cpp +++ b/evmjit/libevmjit/Cache.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -59,6 +60,36 @@ void Cache::clear() fs::remove(it->path()); } +void Cache::preload(llvm::ExecutionEngine& _ee, std::unordered_map& _funcCache) +{ + // TODO: Cache dir should be in one place + using namespace llvm::sys; + llvm::SmallString<256> cachePath; + path::system_temp_directory(false, cachePath); + path::append(cachePath, "evm_objs"); + + // Disable listener + auto listener = g_listener; + g_listener = nullptr; + + std::error_code err; + for (auto it = fs::directory_iterator{cachePath.str(), err}; it != fs::directory_iterator{}; it.increment(err)) + { + auto name = it->path().substr(cachePath.size() + 1); + if (auto module = getObject(name)) + { + DLOG(cache) << "Preload: " << name << "\n"; + _ee.addModule(module.get()); + module.release(); + auto addr = _ee.getFunctionAddress(name); + assert(addr); + _funcCache[std::move(name)] = addr; + } + } + + g_listener = listener; +} + std::unique_ptr Cache::getObject(std::string const& id) { if (g_mode != CacheMode::on && g_mode != CacheMode::read) diff --git a/evmjit/libevmjit/Cache.h b/evmjit/libevmjit/Cache.h index b0d26d080..f6a0a3400 100644 --- a/evmjit/libevmjit/Cache.h +++ b/evmjit/libevmjit/Cache.h @@ -1,9 +1,15 @@ #pragma once #include +#include #include +namespace llvm +{ + class ExecutionEngine; +} + namespace dev { namespace eth @@ -18,7 +24,8 @@ enum class CacheMode off, read, write, - clear + clear, + preload }; class ObjectCache : public llvm::ObjectCache @@ -43,6 +50,9 @@ public: /// Clears cache storage static void clear(); + + /// Loads all available cached objects to ExecutionEngine + static void preload(llvm::ExecutionEngine& _ee, std::unordered_map& _funcCache); }; } diff --git a/evmjit/libevmjit/ExecutionEngine.cpp b/evmjit/libevmjit/ExecutionEngine.cpp index ce1f530d7..0ed4a65b5 100644 --- a/evmjit/libevmjit/ExecutionEngine.cpp +++ b/evmjit/libevmjit/ExecutionEngine.cpp @@ -76,6 +76,7 @@ cl::opt g_cache{"cache", cl::desc{"Cache compiled EVM code on disk"}, clEnumValN(CacheMode::read, "r", "Read only. No new objects are added to cache."), clEnumValN(CacheMode::write, "w", "Write only. No objects are loaded from cache."), clEnumValN(CacheMode::clear, "c", "Clear the cache storage. Cache is disabled."), + clEnumValN(CacheMode::preload, "p", "Preload all cached objects."), clEnumValEnd)}; cl::opt g_stats{"st", cl::desc{"Statistics"}}; cl::opt g_dump{"dump", cl::desc{"Dump LLVM IR module"}}; @@ -111,8 +112,15 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) std::unique_ptr listener{new ExecStats}; listener->stateChanged(ExecState::Started); + bool preloadCache = g_cache == CacheMode::preload; + if (preloadCache) + g_cache = CacheMode::on; + + // TODO: Do not pseudo-init the cache every time auto objectCache = (g_cache != CacheMode::off && g_cache != CacheMode::clear) ? Cache::getObjectCache(g_cache, listener.get()) : nullptr; + static std::unordered_map funcCache; + static std::unique_ptr ee; if (!ee) { @@ -138,6 +146,9 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) return ReturnCode::LLVMConfigError; module.release(); // Successfully created llvm::ExecutionEngine takes ownership of the module ee->setObjectCache(objectCache); + + if (preloadCache) + Cache::preload(*ee, funcCache); } static StatsCollector statsCollector; @@ -146,10 +157,9 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) m_runtime.init(_data, _env); EntryFuncPtr entryFuncPtr = nullptr; - static std::unordered_map funcCache; auto it = funcCache.find(mainFuncName); if (it != funcCache.end()) - entryFuncPtr = it->second; + entryFuncPtr = (EntryFuncPtr) it->second; if (!entryFuncPtr) { @@ -177,7 +187,8 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) if (!CHECK(entryFuncPtr)) return ReturnCode::LLVMLinkError; - funcCache[mainFuncName] = entryFuncPtr; + if (it == funcCache.end()) + funcCache[mainFuncName] = (uint64_t) entryFuncPtr; listener->stateChanged(ExecState::Execution); auto returnCode = entryFuncPtr(&m_runtime); From 95dc6d21b68c21051852f2b576b9d08854da369f Mon Sep 17 00:00:00 2001 From: arkpar Date: Thu, 26 Mar 2015 12:19:21 +0100 Subject: [PATCH 03/11] testing part 1 --- mix/CMakeLists.txt | 44 +- mix/MixApplication.cpp | 71 +++- mix/MixApplication.h | 17 + mix/main.cpp | 15 +- mix/qml.qrc | 2 +- mix/qml/Application.qml | 397 ++++++++++++++++++ mix/test.qrc | 6 + mix/test/TestMain.qml | 51 +++ .../TestTransactionDebug.qml} | 0 9 files changed, 567 insertions(+), 36 deletions(-) create mode 100644 mix/qml/Application.qml create mode 100644 mix/test.qrc create mode 100644 mix/test/TestMain.qml rename mix/{qml/main.qml => test/TestTransactionDebug.qml} (100%) diff --git a/mix/CMakeLists.txt b/mix/CMakeLists.txt index d57dafcac..4cb285b85 100644 --- a/mix/CMakeLists.txt +++ b/mix/CMakeLists.txt @@ -16,7 +16,7 @@ include_directories(${Boost_INCLUDE_DIRS}) include_directories(BEFORE ..) find_package (Qt5WebEngine QUIET) -qt5_add_resources(UI_RESOURCES res.qrc qml.qrc) +qt5_add_resources(UI_RESOURCES res.qrc qml.qrc test.qrc) file(GLOB HEADERS "*.h") @@ -37,32 +37,18 @@ eth_add_executable(${EXECUTABLE} UI_RESOURCES ${UI_RESOURCES} ) -target_link_libraries(${EXECUTABLE} Qt5::Core) -target_link_libraries(${EXECUTABLE} Qt5::Gui) -target_link_libraries(${EXECUTABLE} Qt5::Widgets) -target_link_libraries(${EXECUTABLE} Qt5::Network) -target_link_libraries(${EXECUTABLE} Qt5::Quick) -target_link_libraries(${EXECUTABLE} Qt5::Qml) -target_link_libraries(${EXECUTABLE} webthree) -target_link_libraries(${EXECUTABLE} ethereum) -target_link_libraries(${EXECUTABLE} evm) -target_link_libraries(${EXECUTABLE} ethcore) -target_link_libraries(${EXECUTABLE} devcrypto) -target_link_libraries(${EXECUTABLE} secp256k1) -if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) -target_link_libraries(${EXECUTABLE} serpent) -endif() -target_link_libraries(${EXECUTABLE} lll) -target_link_libraries(${EXECUTABLE} solidity) -target_link_libraries(${EXECUTABLE} evmcore) -target_link_libraries(${EXECUTABLE} devcore) -target_link_libraries(${EXECUTABLE} jsqrc) -target_link_libraries(${EXECUTABLE} web3jsonrpc) +find_package(Qt5QuickTest REQUIRED) +find_package(Qt5Test REQUIRED) +set(LIBRARIES "Qt5::Core;Qt5::Gui;Qt5::Widgets;Qt5::Network;Qt5::Quick;Qt5::Qml;webthree;ethereum;evm;ethcore;devcrypto;solidity;evmcore;devcore;jsqrc;web3jsonrpc") if (${ETH_HAVE_WEBENGINE}) add_definitions(-DETH_HAVE_WEBENGINE) - target_link_libraries(${EXECUTABLE} Qt5::WebEngine) + list(APPEND LIBRARIES "Qt5::WebEngine") endif() + list(APPEND LIBRARIES "Qt5::QuickTest") + list(APPEND LIBRARIES "Qt5::Test") + +target_link_libraries(${EXECUTABLE} ${LIBRARIES}) # eth_install_executable is defined in cmake/EthExecutableHelper.cmake eth_install_executable(${EXECUTABLE} @@ -71,5 +57,15 @@ eth_install_executable(${EXECUTABLE} #add qml asnd stdc files to project tree in Qt creator file(GLOB_RECURSE QMLFILES "qml/*.*") +file(GLOB_RECURSE TESTFILE "test/*.*") file(GLOB_RECURSE SOLFILES "stdc/*.*") -add_custom_target(mix_qml SOURCES ${QMLFILES} ${SOLFILES}) +add_custom_target(mix_qml SOURCES ${QMLFILES} ${SOLFILES} ${TESTFILES}) + + +#test target +#set(TEST_EXECUTABLE mix_test) +#qt5_add_resources(UI_RESOURCES test.qrc) +#add_executable(${TEST_EXECUTABLE} ${UI_RESOURCES} ${SRC_LIST} ${HEADERS}) + + + diff --git a/mix/MixApplication.cpp b/mix/MixApplication.cpp index 028f1cb0b..7eefcc38e 100644 --- a/mix/MixApplication.cpp +++ b/mix/MixApplication.cpp @@ -23,6 +23,10 @@ #include #include #include +#include +#include +#include +#include #ifdef ETH_HAVE_WEBENGINE #include #endif @@ -37,21 +41,73 @@ using namespace dev::mix; +ApplicationService::ApplicationService() +{ +#ifdef ETH_HAVE_WEBENGINE + QtWebEngine::initialize(); +#endif + QFont f; + m_systemPointSize = f.pointSize(); +} + + +#include + +bool ApplicationService::waitForSignal(QObject* _item, QString _signalName, int _timeout) +{ + QSignalSpy spy(_item, ("2" + _signalName.toStdString()).c_str()); + QMetaObject const* mo = _item->metaObject(); + + QStringList methods; + + for(int i = mo->methodOffset(); i < mo->methodCount(); ++i) { + if (mo->method(i).methodType() == QMetaMethod::Signal) { + methods << QString::fromLatin1(mo->method(i).methodSignature()); + } + } + + QElapsedTimer timer; + timer.start(); + + while (!spy.size()) { + int remaining = _timeout - int(timer.elapsed()); + if (remaining <= 0) + break; + QCoreApplication::processEvents(QEventLoop::AllEvents, remaining); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QTest::qSleep(10); + } + + return spy.size(); +} + MixApplication::MixApplication(int& _argc, char* _argv[]): QApplication(_argc, _argv), m_engine(new QQmlApplicationEngine()) +{ + initialize(); + //m_engine->load(QUrl("qrc:/qml/main.qml")); + m_engine->load(QUrl("qrc:/test/TestMain.qml")); + + + if (!m_engine->rootObjects().empty()) + { + QWindow *window = qobject_cast(m_engine->rootObjects().at(0)); + if (window) + window->setIcon(QIcon(":/res/mix_256x256x32.png")); + } +} + + +void MixApplication::initialize() { setOrganizationName(tr("Ethereum")); setOrganizationDomain(tr("ethereum.org")); setApplicationName(tr("Mix")); setApplicationVersion("0.1"); -#ifdef ETH_HAVE_WEBENGINE - QtWebEngine::initialize(); -#endif - QFont f; - m_engine->rootContext()->setContextProperty("systemPointSize", f.pointSize()); qmlRegisterType("org.ethereum.qml.CodeModel", 1, 0, "CodeModel"); qmlRegisterType("org.ethereum.qml.ClientModel", 1, 0, "ClientModel"); + qmlRegisterType("org.ethereum.qml.ApplicationService", 1, 0, "ApplicationService"); qmlRegisterType("org.ethereum.qml.FileIo", 1, 0, "FileIo"); qmlRegisterType("org.ethereum.qml.QEther", 1, 0, "QEther"); qmlRegisterType("org.ethereum.qml.QBigInt", 1, 0, "QBigInt"); @@ -63,10 +119,7 @@ MixApplication::MixApplication(int& _argc, char* _argv[]): qmlRegisterType("HttpServer", 1, 0, "HttpServer"); qRegisterMetaType("CodeModel*"); qRegisterMetaType("ClientModel*"); - - m_engine->load(QUrl("qrc:/qml/main.qml")); - QWindow *window = qobject_cast(m_engine->rootObjects().at(0)); - window->setIcon(QIcon(":/res/mix_256x256x32.png")); + qRegisterMetaType("ClientModel*"); } MixApplication::~MixApplication() diff --git a/mix/MixApplication.h b/mix/MixApplication.h index 49a6e0047..ce3544392 100644 --- a/mix/MixApplication.h +++ b/mix/MixApplication.h @@ -33,12 +33,29 @@ namespace dev namespace mix { +class ApplicationService: public QObject +{ + Q_OBJECT + Q_PROPERTY(int systemPointSize READ systemPointSize CONSTANT) + +public: + ApplicationService(); + int systemPointSize() const { return m_systemPointSize; } +public slots: + bool waitForSignal(QObject* _item, QString _signalName, int _timeout); + +private: + int m_systemPointSize = 0; +}; + + class MixApplication: public QApplication { Q_OBJECT public: MixApplication(int& _argc, char* _argv[]); + static void initialize(); virtual ~MixApplication(); QQmlApplicationEngine* engine() { return m_engine.get(); } diff --git a/mix/main.cpp b/mix/main.cpp index 798520e39..908606bd9 100644 --- a/mix/main.cpp +++ b/mix/main.cpp @@ -24,6 +24,10 @@ #include #include "MixApplication.h" #include "Exceptions.h" + +#include + + using namespace dev::mix; int main(int _argc, char* _argv[]) @@ -43,8 +47,15 @@ int main(int _argc, char* _argv[]) #endif try { - MixApplication app(_argc, _argv); - return app.exec(); + + // + MixApplication::initialize(); + //MixApplication app(_argc, _argv); + return quick_test_main(_argc, _argv, "mix", "/home/arkady/src/cpp-ethereum/mix/test/TestMain.qml"); + + + //MixApplication app(_argc, _argv); + //return app.exec(); } catch (boost::exception const& _e) { diff --git a/mix/qml.qrc b/mix/qml.qrc index bed954741..fd437d881 100644 --- a/mix/qml.qrc +++ b/mix/qml.qrc @@ -1,6 +1,7 @@ qml/AlertMessageDialog.qml + qml/Application.qml qml/BasicMessage.qml qml/BigIntValue.qml qml/CallStack.qml @@ -59,7 +60,6 @@ qml/js/ProjectModel.js qml/js/QEtherHelper.js qml/js/TransactionHelper.js - qml/main.qml qml/qmldir diff --git a/mix/qml/Application.qml b/mix/qml/Application.qml new file mode 100644 index 000000000..b112cf527 --- /dev/null +++ b/mix/qml/Application.qml @@ -0,0 +1,397 @@ +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Controls.Styles 1.1 +import QtQuick.Dialogs 1.1 +import QtQuick.Layouts 1.1 +import QtQuick.Window 2.1 +import QtQuick.PrivateWidgets 1.1 +import Qt.labs.settings 1.0 +import org.ethereum.qml.QEther 1.0 +import org.ethereum.qml.CodeModel 1.0 +import org.ethereum.qml.ClientModel 1.0 +import org.ethereum.qml.FileIo 1.0 +import org.ethereum.qml.Clipboard 1.0 +import org.ethereum.qml.ApplicationService 1.0 + +ApplicationWindow { + + id: mainApplication + signal loaded; + visible: true + width: 1200 + height: 800 + minimumWidth: 400 + minimumHeight: 300 + title: qsTr("Mix") + property alias systemPointSize: appService.systemPointSize; + property alias mainContent: mainContent; + property alias codeModel: codeModel; + property alias clientModel: clientModel; + property alias projectModel: projectModel; + property alias appService: appService; + + ApplicationService { + id: appService + } + + CodeModel { + id: codeModel + } + + ClientModel { + id: clientModel + codeModel: codeModel + } + + ProjectModel { + id: projectModel + } + + FileIo { + id: fileIo + } + + Clipboard { + id: clipboard + } + + Connections { + target: mainApplication + onClosing: + { + mainApplication.close(); + close.accepted = false; + } + } + + Component.onCompleted: { + loaded(); + } + + function close() { + projectModel.appIsClosing = true; + if (projectModel.projectPath !== "") + projectModel.closeProject(function() { Qt.quit(); }) + else + Qt.quit(); + } + + menuBar: MenuBar { + Menu { + title: qsTr("File") + MenuItem { action: createProjectAction } + MenuItem { action: openProjectAction } + MenuSeparator {} + MenuItem { action: saveAllFilesAction } + MenuItem { action: saveCurrentDocument } + MenuSeparator {} + MenuItem { action: addExistingFileAction } + MenuItem { action: addNewJsFileAction } + MenuItem { action: addNewHtmlFileAction } + MenuItem { action: addNewCssFileAction } + MenuSeparator {} + MenuItem { action: addNewContractAction } + MenuItem { action: closeProjectAction } + MenuSeparator {} + MenuItem { action: exitAppAction } + } + Menu { + title: qsTr("Deploy") + MenuItem { action: mineAction } + MenuSeparator {} + MenuItem { action: editStatesAction } + MenuSeparator {} + MenuItem { action: deployViaRpcAction } + MenuSeparator {} + MenuItem { action: toggleRunOnLoadAction } + } + Menu { + title: qsTr("Debug") + MenuItem { action: debugRunAction } + MenuSeparator {} + MenuItem { action: toggleAssemblyDebuggingAction } + } + Menu { + title: qsTr("Windows") + MenuItem { action: openNextDocumentAction } + MenuItem { action: openPrevDocumentAction } + MenuSeparator {} + MenuItem { action: toggleProjectNavigatorAction } + MenuItem { action: showHideRightPanelAction } + MenuItem { action: toggleTransactionLogAction } + MenuItem { action: toggleWebPreviewAction } + MenuItem { action: toggleWebPreviewOrientationAction } + //MenuItem { action: toggleCallsInLog } + } + } + + MainContent { + id: mainContent; + anchors.fill: parent + } + + ModalDialog { + objectName: "dialog" + id: dialog + } + + AlertMessageDialog { + objectName: "alertMessageDialog" + id: messageDialog + } + + Settings { + id: mainWindowSettings + property alias mainWidth: mainApplication.width + property alias mainHeight: mainApplication.height + property alias mainX: mainApplication.x + property alias mainY: mainApplication.y + } + + Action { + id: exitAppAction + text: qsTr("Exit") + shortcut: "Ctrl+Q" + onTriggered: + { + mainApplication.close(); + } + } + + Action { + id: mineAction + text: qsTr("New Block") + shortcut: "Ctrl+M" + onTriggered: clientModel.mine(); + enabled: codeModel.hasContract && !clientModel.running && !clientModel.mining + } + + StateList { + id: stateList + } + + Action { + id: editStatesAction + text: qsTr("Edit States") + shortcut: "Ctrl+Alt+E" + onTriggered: stateList.show(); + } + + Connections { + target: projectModel.stateListModel + + function updateRunLabel() + { + debugRunAction.text = qsTr("Deploy") + " \"" + projectModel.stateListModel.defaultStateName() + "\""; + } + + onDefaultStateChanged: updateRunLabel() + onStateListModelReady: updateRunLabel() + } + + Action { + id: debugRunAction + text: qsTr("Deploy") + shortcut: "F5" + onTriggered: mainContent.startQuickDebugging() + enabled: codeModel.hasContract && !clientModel.running + } + + Action { + id: toggleAssemblyDebuggingAction + text: qsTr("Show VM Code") + shortcut: "Ctrl+Alt+V" + onTriggered: mainContent.rightPane.assemblyMode = !mainContent.rightPane.assemblyMode; + checked: mainContent.rightPane.assemblyMode; + enabled: true + } + + Action { + id: toggleWebPreviewAction + text: qsTr("Show Web View") + shortcut: "F2" + checkable: true + checked: mainContent.webViewVisible + onTriggered: mainContent.toggleWebPreview(); + } + + Action { + id: toggleTransactionLogAction + text: qsTr("Show States and Transactions") + shortcut: "Alt+1" + checkable: true + checked: mainContent.rightPane.transactionLog.visible + onTriggered: mainContent.rightPane.transactionLog.visible = !mainContent.rightPane.transactionLog.visible + } + + Action { + id: toggleProjectNavigatorAction + text: qsTr("Show Project Navigator") + shortcut: "Alt+0" + checkable: true + checked: mainContent.projectViewVisible + onTriggered: mainContent.toggleProjectView(); + } + + Action { + id: toggleWebPreviewOrientationAction + text: qsTr("Horizontal Web View") + shortcut: "" + checkable: true + checked: mainContent.webViewHorizontal + onTriggered: mainContent.toggleWebPreviewOrientation(); + } + + Action { + id: toggleRunOnLoadAction + text: qsTr("Load State on Startup") + shortcut: "" + checkable: true + checked: mainContent.runOnProjectLoad + onTriggered: mainContent.runOnProjectLoad = !mainContent.runOnProjectLoad + } + + Action { + id: showHideRightPanelAction + text: qsTr("Show Right View") + shortcut: "F7" + checkable: true + checked: mainContent.rightViewVisible + onTriggered: mainContent.toggleRightView(); + } + + Action { + id: createProjectAction + text: qsTr("&New Project") + shortcut: "Ctrl+N" + enabled: true; + onTriggered: projectModel.createProject(); + } + + Action { + id: openProjectAction + text: qsTr("&Open Project") + shortcut: "Ctrl+O" + enabled: true; + onTriggered: openProjectFileDialog.open() + } + + FileDialog { + id: openProjectFileDialog + visible: false + title: qsTr("Open a Project") + selectFolder: true + onAccepted: { + var path = openProjectFileDialog.fileUrl.toString(); + path += "/"; + projectModel.loadProject(path); + } + } + + Action { + id: addNewJsFileAction + text: qsTr("New JavaScript File") + shortcut: "Ctrl+Alt+J" + enabled: !projectModel.isEmpty + onTriggered: projectModel.newJsFile(); + } + + Action { + id: addNewHtmlFileAction + text: qsTr("New HTML File") + shortcut: "Ctrl+Alt+H" + enabled: !projectModel.isEmpty + onTriggered: projectModel.newHtmlFile(); + } + + Action { + id: addNewCssFileAction + text: qsTr("New CSS File") + shortcut: "Ctrl+Alt+S" + enabled: !projectModel.isEmpty + onTriggered: projectModel.newCssFile(); + } + + Action { + id: addNewContractAction + text: qsTr("New Contract") + shortcut: "Ctrl+Alt+C" + enabled: !projectModel.isEmpty + onTriggered: projectModel.newContract(); + } + + Action { + id: addExistingFileAction + text: qsTr("Add Existing File") + shortcut: "Ctrl+Alt+A" + enabled: !projectModel.isEmpty + onTriggered: addExistingFileDialog.open() + } + + FileDialog { + id: addExistingFileDialog + visible: false + title: qsTr("Add a File") + selectFolder: false + onAccepted: { + var paths = addExistingFileDialog.fileUrls; + projectModel.addExistingFiles(paths); + } + } + + Action { + id: saveAllFilesAction + text: qsTr("Save All") + shortcut: "Ctrl+Shift+A" + enabled: !projectModel.isEmpty + onTriggered: projectModel.saveAll(); + } + + Action { + id: saveCurrentDocument + text: qsTr("Save Current Document") + shortcut: "Ctrl+S" + enabled: !projectModel.isEmpty + onTriggered: projectModel.saveCurrentDocument(); + } + + Action { + id: closeProjectAction + text: qsTr("Close Project") + shortcut: "Ctrl+W" + enabled: !projectModel.isEmpty + onTriggered: projectModel.closeProject(); + } + + Action { + id: openNextDocumentAction + text: qsTr("Next Document") + shortcut: "Ctrl+Tab" + enabled: !projectModel.isEmpty + onTriggered: projectModel.openNextDocument(); + } + + Action { + id: openPrevDocumentAction + text: qsTr("Previous Document") + shortcut: "Ctrl+Shift+Tab" + enabled: !projectModel.isEmpty + onTriggered: projectModel.openPrevDocument(); + } + + Action { + id: toggleBreakpointAction + text: qsTr("Toggle Breakpoint") + shortcut: "F9" + enabled: mainContent.codeEditor.editingContract(); + onTriggered: mainContent.toggleBreakpoint(); + } + + Action { + id: deployViaRpcAction + text: qsTr("Deploy to Network") + shortcut: "Ctrl+Shift+D" + enabled: !projectModel.isEmpty && codeModel.hasContract + onTriggered: projectModel.deployProject(); + } +} diff --git a/mix/test.qrc b/mix/test.qrc new file mode 100644 index 000000000..e7d2271a4 --- /dev/null +++ b/mix/test.qrc @@ -0,0 +1,6 @@ + + + test/TestMain.qml + test/TestTransactionDebug.qml + + diff --git a/mix/test/TestMain.qml b/mix/test/TestMain.qml new file mode 100644 index 000000000..737a2b2a4 --- /dev/null +++ b/mix/test/TestMain.qml @@ -0,0 +1,51 @@ +import QtQuick 2.2 +import QtTest 1.1 +//import Qt.test.qtestroot 1.0 +import "../qml" + + +TestCase +{ + +Item +{ + id: helper + function findChild(item, childId) { + if (item.children) { + var searchString = "button" + + for (var idx in item.children) { + var currentItem = item.children[idx] + + if (currentItem.id.match("^"+childId) === childId) + return currentItem; + + return findChild(currentItem, childId); + } + } + } +} + + +function test_t1() +{ + waitForRendering(mainApplication.mainContent, 10000); + mainApplication.projectModel.createProject(); + var projectDlg = helper.findChild(mainApplication, "newProjectDialog"); + + if (mainApplication.appService.waitForSignal(mainApplication.codeModel, "compilationComplete()", 5000)) + console.log("compiled"); +} + +function runTest() +{ + waitForRendering(mainApplication.mainContent, 10000); + console.log("runtest"); + mousePress(mainApplication, 10, 10); +} + +Application { + id: mainApplication +} + +} diff --git a/mix/qml/main.qml b/mix/test/TestTransactionDebug.qml similarity index 100% rename from mix/qml/main.qml rename to mix/test/TestTransactionDebug.qml From 57e88359dc579ac56f5b1272f00fad86cf0b279d Mon Sep 17 00:00:00 2001 From: arkpar Date: Sat, 28 Mar 2015 10:36:47 +0100 Subject: [PATCH 04/11] test framework part2 --- mix/CMakeLists.txt | 23 ++-- mix/ClientModel.cpp | 5 +- mix/ClientModel.h | 2 + mix/MixApplication.cpp | 51 +++------ mix/MixApplication.h | 8 +- mix/QBasicNodeDefinition.cpp | 41 +++++++ mix/QBasicNodeDefinition.h | 12 ++- mix/QVariableDeclaration.cpp | 1 + mix/QVariableDeclaration.h | 5 +- mix/QVariableDefinition.cpp | 36 +++++++ mix/QVariableDefinition.h | 8 +- mix/main.cpp | 29 +---- mix/qml.qrc | 1 - mix/qml/Application.qml | 4 + mix/qml/CallStack.qml | 5 +- mix/qml/CodeEditor.qml | 51 +++++---- mix/qml/CodeEditorStyle.qml | 1 - mix/qml/CodeEditorView.qml | 15 ++- mix/qml/CommonSeparator.qml | 2 +- mix/qml/Debugger.qml | 12 ++- mix/qml/DebuggerPaneStyle.qml | 1 - mix/qml/DefaultLabel.qml | 1 - mix/qml/DeploymentDialog.qml | 4 +- mix/qml/FilesSection.qml | 30 +++--- mix/qml/LogsPane.qml | 72 +++++++------ mix/qml/LogsPaneStyle.qml | 1 - mix/qml/NewProjectDialog.qml | 1 + mix/qml/ProjectFilesStyle.qml | 1 - mix/qml/ProjectList.qml | 21 ++-- mix/qml/ProjectModel.qml | 1 + mix/qml/Splitter.qml | 3 - mix/qml/StateDialog.qml | 6 +- mix/qml/StateDialogStyle.qml | 1 - mix/qml/StatusPane.qml | 6 +- mix/qml/StatusPaneStyle.qml | 1 - mix/qml/Style.qml | 1 - mix/qml/TransactionDialog.qml | 6 +- mix/qml/WebCodeEditor.qml | 16 +-- mix/qml/WebPreview.qml | 20 ++-- mix/qml/WebPreviewStyle.qml | 1 - mix/qml/qmldir | 8 -- mix/test/TestMain.cpp | 50 +++++++++ mix/test/TestMain.qml | 51 --------- mix/test/TestService.cpp | 113 ++++++++++++++++++++ mix/test/TestService.h | 52 +++++++++ mix/test/qml/TestMain.qml | 52 +++++++++ mix/test/{ => qml}/TestTransactionDebug.qml | 0 mix/web.qrc | 2 +- 48 files changed, 566 insertions(+), 268 deletions(-) create mode 100644 mix/QBasicNodeDefinition.cpp create mode 100644 mix/QVariableDefinition.cpp delete mode 100644 mix/qml/qmldir create mode 100644 mix/test/TestMain.cpp delete mode 100644 mix/test/TestMain.qml create mode 100644 mix/test/TestService.cpp create mode 100644 mix/test/TestService.h create mode 100644 mix/test/qml/TestMain.qml rename mix/test/{ => qml}/TestTransactionDebug.qml (100%) diff --git a/mix/CMakeLists.txt b/mix/CMakeLists.txt index 4cb285b85..8137c8eba 100644 --- a/mix/CMakeLists.txt +++ b/mix/CMakeLists.txt @@ -16,7 +16,7 @@ include_directories(${Boost_INCLUDE_DIRS}) include_directories(BEFORE ..) find_package (Qt5WebEngine QUIET) -qt5_add_resources(UI_RESOURCES res.qrc qml.qrc test.qrc) +qt5_add_resources(UI_RESOURCES res.qrc qml.qrc) file(GLOB HEADERS "*.h") @@ -37,16 +37,12 @@ eth_add_executable(${EXECUTABLE} UI_RESOURCES ${UI_RESOURCES} ) -find_package(Qt5QuickTest REQUIRED) -find_package(Qt5Test REQUIRED) set(LIBRARIES "Qt5::Core;Qt5::Gui;Qt5::Widgets;Qt5::Network;Qt5::Quick;Qt5::Qml;webthree;ethereum;evm;ethcore;devcrypto;solidity;evmcore;devcore;jsqrc;web3jsonrpc") if (${ETH_HAVE_WEBENGINE}) add_definitions(-DETH_HAVE_WEBENGINE) list(APPEND LIBRARIES "Qt5::WebEngine") endif() - list(APPEND LIBRARIES "Qt5::QuickTest") - list(APPEND LIBRARIES "Qt5::Test") target_link_libraries(${EXECUTABLE} ${LIBRARIES}) @@ -57,15 +53,22 @@ eth_install_executable(${EXECUTABLE} #add qml asnd stdc files to project tree in Qt creator file(GLOB_RECURSE QMLFILES "qml/*.*") -file(GLOB_RECURSE TESTFILE "test/*.*") +file(GLOB_RECURSE TESTFILES "test/qml/*.*") file(GLOB_RECURSE SOLFILES "stdc/*.*") add_custom_target(mix_qml SOURCES ${QMLFILES} ${SOLFILES} ${TESTFILES}) - #test target -#set(TEST_EXECUTABLE mix_test) -#qt5_add_resources(UI_RESOURCES test.qrc) -#add_executable(${TEST_EXECUTABLE} ${UI_RESOURCES} ${SRC_LIST} ${HEADERS}) +find_package(Qt5QuickTest REQUIRED) +find_package(Qt5Test REQUIRED) +set(TEST_EXECUTABLE mix_test) +list(APPEND LIBRARIES "Qt5::QuickTest") +list(APPEND LIBRARIES "Qt5::Test") +list(REMOVE_ITEM SRC_LIST "./main.cpp") +aux_source_directory(test SRC_LIST) +file(GLOB HEADERS "test/*.h") +add_executable(${TEST_EXECUTABLE} ${UI_RESOURCES} ${SRC_LIST} ${HEADERS}) +target_link_libraries(${TEST_EXECUTABLE} ${LIBRARIES}) +set_target_properties(${TEST_EXECUTABLE} PROPERTIES EXCLUDE_FROM_ALL 1 EXCLUDE_FROM_DEFAULT_BUILD 1) diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index 739eb8d8c..5a344f921 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -90,6 +90,7 @@ ClientModel::ClientModel(): ClientModel::~ClientModel() { + m_runFuture.waitForFinished(); } QString ClientModel::apiCall(QString const& _message) @@ -113,7 +114,7 @@ void ClientModel::mine() m_mining = true; emit miningStarted(); emit miningStateChanged(); - QtConcurrent::run([=]() + m_runFuture = QtConcurrent::run([=]() { try { @@ -206,7 +207,7 @@ void ClientModel::executeSequence(std::vector const& _seque emit runStateChanged(); //run sequence - QtConcurrent::run([=]() + m_runFuture = QtConcurrent::run([=]() { try { diff --git a/mix/ClientModel.h b/mix/ClientModel.h index a5d89d859..9b35af4fd 100644 --- a/mix/ClientModel.h +++ b/mix/ClientModel.h @@ -27,6 +27,7 @@ #include #include #include +#include #include "MachineStates.h" namespace dev @@ -203,6 +204,7 @@ private: std::atomic m_running; std::atomic m_mining; + QFuture m_runFuture; std::unique_ptr m_client; std::unique_ptr m_rpcConnector; std::unique_ptr m_web3Server; diff --git a/mix/MixApplication.cpp b/mix/MixApplication.cpp index 7eefcc38e..80929dc49 100644 --- a/mix/MixApplication.cpp +++ b/mix/MixApplication.cpp @@ -24,9 +24,6 @@ #include #include #include -#include -#include -#include #ifdef ETH_HAVE_WEBENGINE #include #endif @@ -50,45 +47,10 @@ ApplicationService::ApplicationService() m_systemPointSize = f.pointSize(); } - -#include - -bool ApplicationService::waitForSignal(QObject* _item, QString _signalName, int _timeout) -{ - QSignalSpy spy(_item, ("2" + _signalName.toStdString()).c_str()); - QMetaObject const* mo = _item->metaObject(); - - QStringList methods; - - for(int i = mo->methodOffset(); i < mo->methodCount(); ++i) { - if (mo->method(i).methodType() == QMetaMethod::Signal) { - methods << QString::fromLatin1(mo->method(i).methodSignature()); - } - } - - QElapsedTimer timer; - timer.start(); - - while (!spy.size()) { - int remaining = _timeout - int(timer.elapsed()); - if (remaining <= 0) - break; - QCoreApplication::processEvents(QEventLoop::AllEvents, remaining); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); - QTest::qSleep(10); - } - - return spy.size(); -} - MixApplication::MixApplication(int& _argc, char* _argv[]): QApplication(_argc, _argv), m_engine(new QQmlApplicationEngine()) { - initialize(); - //m_engine->load(QUrl("qrc:/qml/main.qml")); - m_engine->load(QUrl("qrc:/test/TestMain.qml")); - - + m_engine->load(QUrl("qrc:/qml/Application.qml")); if (!m_engine->rootObjects().empty()) { QWindow *window = qobject_cast(m_engine->rootObjects().at(0)); @@ -100,6 +62,17 @@ MixApplication::MixApplication(int& _argc, char* _argv[]): void MixApplication::initialize() { +#if __linux + //work around ubuntu appmenu-qt5 bug + //https://bugs.launchpad.net/ubuntu/+source/appmenu-qt5/+bug/1323853 + putenv((char*)"QT_QPA_PLATFORMTHEME="); + putenv((char*)"QSG_RENDER_LOOP=threaded"); +#endif +#if (defined(_WIN32) || defined(_WIN64)) + if (!getenv("OPENSSL_CONF")) + putenv((char*)"OPENSSL_CONF=c:\\"); +#endif + setOrganizationName(tr("Ethereum")); setOrganizationDomain(tr("ethereum.org")); setApplicationName(tr("Mix")); diff --git a/mix/MixApplication.h b/mix/MixApplication.h index ce3544392..9756f1b89 100644 --- a/mix/MixApplication.h +++ b/mix/MixApplication.h @@ -37,12 +37,16 @@ class ApplicationService: public QObject { Q_OBJECT Q_PROPERTY(int systemPointSize READ systemPointSize CONSTANT) + Q_PROPERTY(bool haveWebEngine READ haveWebEngine CONSTANT) public: ApplicationService(); int systemPointSize() const { return m_systemPointSize; } -public slots: - bool waitForSignal(QObject* _item, QString _signalName, int _timeout); +#ifdef ETH_HAVE_WEBENGINE + bool haveWebEngine() const { return true; } +#else + bool haveWebEngine() const { return false; } +#endif private: int m_systemPointSize = 0; diff --git a/mix/QBasicNodeDefinition.cpp b/mix/QBasicNodeDefinition.cpp new file mode 100644 index 000000000..8905caef6 --- /dev/null +++ b/mix/QBasicNodeDefinition.cpp @@ -0,0 +1,41 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file QBasicNodeDefinition.cpp + * @author Yann yann@ethdev.com + * @date 2014 + */ + +#include "QBasicNodeDefinition.h" +#include + +namespace dev +{ +namespace mix +{ + +QBasicNodeDefinition::QBasicNodeDefinition(QObject* _parent, solidity::Declaration const* _d): + QObject(_parent), m_name(QString::fromStdString(_d->getName())) +{ +} + +QBasicNodeDefinition::QBasicNodeDefinition(QObject* _parent, std::string const& _name): + QObject(_parent), m_name(QString::fromStdString(_name)) +{ +} + +} +} diff --git a/mix/QBasicNodeDefinition.h b/mix/QBasicNodeDefinition.h index 42f8ac15d..eb81d4f85 100644 --- a/mix/QBasicNodeDefinition.h +++ b/mix/QBasicNodeDefinition.h @@ -21,11 +21,17 @@ #pragma once +#include #include -#include namespace dev { + +namespace solidity +{ +class Declaration; +} + namespace mix { @@ -37,8 +43,8 @@ class QBasicNodeDefinition: public QObject public: QBasicNodeDefinition(QObject* _parent = nullptr): QObject(_parent) {} ~QBasicNodeDefinition() {} - QBasicNodeDefinition(QObject* _parent, solidity::Declaration const* _d): QObject(_parent), m_name(QString::fromStdString(_d->getName())) {} - QBasicNodeDefinition(QObject* _parent, std::string const& _name): QObject(_parent), m_name(QString::fromStdString(_name)) {} + QBasicNodeDefinition(QObject* _parent, solidity::Declaration const* _d); + QBasicNodeDefinition(QObject* _parent, std::string const& _name); /// Get the name of the node. QString name() const { return m_name; } diff --git a/mix/QVariableDeclaration.cpp b/mix/QVariableDeclaration.cpp index 391d26c04..3bbb0d523 100644 --- a/mix/QVariableDeclaration.cpp +++ b/mix/QVariableDeclaration.cpp @@ -21,6 +21,7 @@ */ #include "QVariableDeclaration.h" +#include #include "CodeModel.h" namespace dev diff --git a/mix/QVariableDeclaration.h b/mix/QVariableDeclaration.h index cf9345061..4309550b2 100644 --- a/mix/QVariableDeclaration.h +++ b/mix/QVariableDeclaration.h @@ -26,14 +26,15 @@ #pragma once +namespace dev +{ + namespace solidity { class Type; class VariableDeclaration; } -namespace dev -{ namespace mix { diff --git a/mix/QVariableDefinition.cpp b/mix/QVariableDefinition.cpp new file mode 100644 index 000000000..4df31ea92 --- /dev/null +++ b/mix/QVariableDefinition.cpp @@ -0,0 +1,36 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file QVariableDefinition.cpp + * @author Yann yann@ethdev.com + * @date 2014 + */ + +#include "QVariableDefinition.h" +#include + +namespace dev +{ +namespace mix +{ +QString QVariableDefinition::encodeValueAsString() +{ + return QString::fromStdString(dev::toHex(encodeValue())); +} + +} +} + diff --git a/mix/QVariableDefinition.h b/mix/QVariableDefinition.h index 70a663ee3..e6c38f971 100644 --- a/mix/QVariableDefinition.h +++ b/mix/QVariableDefinition.h @@ -21,14 +21,14 @@ #pragma once -#include -#include "QBigInt.h" -#include "QVariableDeclaration.h" +#include +#include namespace dev { namespace mix { +class QVariableDeclaration; class QVariableDefinition: public QObject { @@ -54,7 +54,7 @@ public: /// Decode the return value @a _rawValue. virtual void decodeValue(dev::bytes const& _rawValue) = 0; /// returns String representation of the encoded value. - Q_INVOKABLE QString encodeValueAsString() { return QString::fromStdString(dev::toHex(encodeValue())); } + Q_INVOKABLE QString encodeValueAsString(); protected: QString m_value; diff --git a/mix/main.cpp b/mix/main.cpp index 908606bd9..78b5261ac 100644 --- a/mix/main.cpp +++ b/mix/main.cpp @@ -22,40 +22,19 @@ #include #include +#include +#include #include "MixApplication.h" -#include "Exceptions.h" - -#include - using namespace dev::mix; int main(int _argc, char* _argv[]) { -#ifdef ETH_HAVE_WEBENGINE - Q_INIT_RESOURCE(js); -#endif -#if __linux - //work around ubuntu appmenu-qt5 bug - //https://bugs.launchpad.net/ubuntu/+source/appmenu-qt5/+bug/1323853 - putenv((char*)"QT_QPA_PLATFORMTHEME="); - putenv((char*)"QSG_RENDER_LOOP=threaded"); -#endif -#if (defined(_WIN32) || defined(_WIN64)) - if (!getenv("OPENSSL_CONF")) - putenv((char*)"OPENSSL_CONF=c:\\"); -#endif try { - - // MixApplication::initialize(); - //MixApplication app(_argc, _argv); - return quick_test_main(_argc, _argv, "mix", "/home/arkady/src/cpp-ethereum/mix/test/TestMain.qml"); - - - //MixApplication app(_argc, _argv); - //return app.exec(); + MixApplication app(_argc, _argv); + return app.exec(); } catch (boost::exception const& _e) { diff --git a/mix/qml.qrc b/mix/qml.qrc index fd437d881..0e8c37b42 100644 --- a/mix/qml.qrc +++ b/mix/qml.qrc @@ -60,6 +60,5 @@ qml/js/ProjectModel.js qml/js/QEtherHelper.js qml/js/TransactionHelper.js - qml/qmldir diff --git a/mix/qml/Application.qml b/mix/qml/Application.qml index b112cf527..b7a065cdf 100644 --- a/mix/qml/Application.qml +++ b/mix/qml/Application.qml @@ -55,6 +55,10 @@ ApplicationWindow { id: clipboard } + Style { + id: appStyle + } + Connections { target: mainApplication onClosing: diff --git a/mix/qml/CallStack.qml b/mix/qml/CallStack.qml index c9e22532d..9a938078c 100644 --- a/mix/qml/CallStack.qml +++ b/mix/qml/CallStack.qml @@ -2,7 +2,6 @@ import QtQuick 2.2 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.1 import QtQuick.Layouts 1.1 -import "." DebugInfoList { @@ -37,7 +36,7 @@ DebugInfoList anchors.leftMargin: 5 color: "#4a4a4a" text: styleData.row; - font.pointSize: DebuggerPaneStyle.general.basicFontSize + font.pointSize: dbgStyle.general.basicFontSize width: parent.width - 5 elide: Text.ElideRight } @@ -58,7 +57,7 @@ DebugInfoList color: "#4a4a4a" text: styleData.value; elide: Text.ElideRight - font.pointSize: DebuggerPaneStyle.general.basicFontSize + font.pointSize: dbgStyle.general.basicFontSize } } } diff --git a/mix/qml/CodeEditor.qml b/mix/qml/CodeEditor.qml index 9d3df69e5..25f76b701 100644 --- a/mix/qml/CodeEditor.qml +++ b/mix/qml/CodeEditor.qml @@ -24,31 +24,36 @@ Item { id: contentView width: parent.width height: parent.height * 0.7 - Rectangle { - id: lineColumn - property int rowHeight: codeEditor.font.pixelSize + 3 - color: "#202020" - width: 50 - height: parent.height - Column { - y: -codeEditor.flickableItem.contentY + 4 - width: parent.width - Repeater { - model: Math.max(codeEditor.lineCount + 2, (lineColumn.height/lineColumn.rowHeight)) - delegate: Text { - id: text - color: codeEditor.textColor - font: codeEditor.font - width: lineColumn.width - 4 - horizontalAlignment: Text.AlignRight - verticalAlignment: Text.AlignVCenter - height: lineColumn.rowHeight - renderType: Text.NativeRendering - text: index + 1 - } + + CodeEditorStyle { + id: style + } + + Rectangle { + id: lineColumn + property int rowHeight: codeEditor.font.pixelSize + 3 + color: "#202020" + width: 50 + height: parent.height + Column { + y: -codeEditor.flickableItem.contentY + 4 + width: parent.width + Repeater { + model: Math.max(codeEditor.lineCount + 2, (lineColumn.height/lineColumn.rowHeight)) + delegate: Text { + id: text + color: codeEditor.textColor + font: codeEditor.font + width: lineColumn.width - 4 + horizontalAlignment: Text.AlignRight + verticalAlignment: Text.AlignVCenter + height: lineColumn.rowHeight + renderType: Text.NativeRendering + text: index + 1 } } } + } TextArea { id: codeEditor @@ -66,7 +71,7 @@ Item { height: parent.height font.family: "Monospace" - font.pointSize: CodeEditorStyle.general.basicFontSize + font.pointSize: style.general.basicFontSize width: parent.width tabChangesFocus: false diff --git a/mix/qml/CodeEditorStyle.qml b/mix/qml/CodeEditorStyle.qml index c9740957c..999054d55 100644 --- a/mix/qml/CodeEditorStyle.qml +++ b/mix/qml/CodeEditorStyle.qml @@ -1,4 +1,3 @@ -pragma Singleton import QtQuick 2.0 QtObject { diff --git a/mix/qml/CodeEditorView.qml b/mix/qml/CodeEditorView.qml index 4d8559200..0f7f5d864 100644 --- a/mix/qml/CodeEditorView.qml +++ b/mix/qml/CodeEditorView.qml @@ -10,6 +10,7 @@ Item { signal documentEdit(string documentId) signal breakpointsChanged(string documentId) signal isCleanChanged(var isClean, string documentId) + signal loadComplete function getDocumentText(documentId) { for (var i = 0; i < editorListModel.count; i++) { @@ -43,6 +44,9 @@ Item { function doLoadDocument(editor, document) { var data = fileIo.readFile(document.path); + editor.onLoadComplete.connect(function() { + loadComplete(); + }); editor.onEditorTextChanged.connect(function() { documentEdit(document.documentId); if (document.isContract) @@ -160,6 +164,11 @@ Item { } } + CodeEditorStyle + { + id: style; + } + MessageDialog { id: messageDialog @@ -177,12 +186,16 @@ Item { Repeater { id: editors model: editorListModel + onItemRemoved: { + console.log("unloaded"); + item.item.unloaded = true; + } delegate: Loader { id: loader active: false asynchronous: true anchors.fill: parent - source: "CodeEditor.qml" + source: appService.haveWebEngine ? "WebCodeEditor.qml" : "CodeEditor.qml" visible: (index >= 0 && index < editorListModel.count && currentDocumentId === editorListModel.get(index).documentId) property bool changed: false onVisibleChanged: { diff --git a/mix/qml/CommonSeparator.qml b/mix/qml/CommonSeparator.qml index c81a81f63..6644802c7 100644 --- a/mix/qml/CommonSeparator.qml +++ b/mix/qml/CommonSeparator.qml @@ -4,6 +4,6 @@ import "." Rectangle { height: 1 - color: Style.generic.layout.separatorColor + color: appStyle.generic.layout.separatorColor } diff --git a/mix/qml/Debugger.qml b/mix/qml/Debugger.qml index 358750b24..aee23b129 100644 --- a/mix/qml/Debugger.qml +++ b/mix/qml/Debugger.qml @@ -65,6 +65,10 @@ Rectangle { Debugger.setBreakpoints(bp); } + DebuggerPaneStyle { + id: dbgStyle + } + Connections { target: clientModel onDebugDataReady: { @@ -422,7 +426,7 @@ Rectangle { color: "#b2b3ae" text: styleData.value.split(' ')[0] font.family: "monospace" - font.pointSize: DebuggerPaneStyle.general.basicFontSize + font.pointSize: dbgStyle.general.basicFontSize wrapMode: Text.NoWrap id: id } @@ -432,7 +436,7 @@ Rectangle { color: styleData.selected ? "white" : "black" font.family: "monospace" text: styleData.value.replace(styleData.value.split(' ')[0], '') - font.pointSize: DebuggerPaneStyle.general.basicFontSize + font.pointSize: dbgStyle.general.basicFontSize } } } @@ -505,7 +509,7 @@ Rectangle { font.family: "monospace" color: "#4a4a4a" text: styleData.row; - font.pointSize: DebuggerPaneStyle.general.basicFontSize + font.pointSize: dbgStyle.general.basicFontSize } } @@ -523,7 +527,7 @@ Rectangle { anchors.verticalCenter: parent.verticalCenter color: "#4a4a4a" text: styleData.value - font.pointSize: DebuggerPaneStyle.general.basicFontSize + font.pointSize: dbgStyle.general.basicFontSize } } } diff --git a/mix/qml/DebuggerPaneStyle.qml b/mix/qml/DebuggerPaneStyle.qml index 1078df2f9..db8bbe253 100644 --- a/mix/qml/DebuggerPaneStyle.qml +++ b/mix/qml/DebuggerPaneStyle.qml @@ -1,4 +1,3 @@ -pragma Singleton import QtQuick 2.0 QtObject { diff --git a/mix/qml/DefaultLabel.qml b/mix/qml/DefaultLabel.qml index d8ef1faff..5d00df137 100644 --- a/mix/qml/DefaultLabel.qml +++ b/mix/qml/DefaultLabel.qml @@ -1,6 +1,5 @@ import QtQuick 2.0 import QtQuick.Controls 1.1 -import "." Label { text: text diff --git a/mix/qml/DeploymentDialog.qml b/mix/qml/DeploymentDialog.qml index c194977cf..1d6771b4a 100644 --- a/mix/qml/DeploymentDialog.qml +++ b/mix/qml/DeploymentDialog.qml @@ -31,7 +31,7 @@ Window { property string currentAccount property alias gasToUse: gasToUseInput.text - color: Style.generic.layout.backgroundColor + color: appStyle.generic.layout.backgroundColor function close() { @@ -329,7 +329,7 @@ Window { anchors.verticalCenter: parent.verticalCenter; anchors.left: applicationUrlEth.right font.italic: true - font.pointSize: Style.absoluteSize(-1) + font.pointSize: appStyle.absoluteSize(-1) } } } diff --git a/mix/qml/FilesSection.qml b/mix/qml/FilesSection.qml index d9f664894..d89875583 100644 --- a/mix/qml/FilesSection.qml +++ b/mix/qml/FilesSection.qml @@ -18,21 +18,21 @@ Rectangle property string sectionName; property variant selManager; property int index; - color: index % 2 === 0 ? "transparent" : ProjectFilesStyle.title.background + color: index % 2 === 0 ? "transparent" : projectFilesStyle.title.background function hiddenHeightTopLevel() { - return section.state === "hidden" ? ProjectFilesStyle.documentsList.height : ProjectFilesStyle.documentsList.fileNameHeight * model.count + ProjectFilesStyle.documentsList.height; + return section.state === "hidden" ? projectFilesStyle.documentsList.height : projectFilesStyle.documentsList.fileNameHeight * model.count + projectFilesStyle.documentsList.height; } function hiddenHeightRepeater() { - return section.state === "hidden" ? 0 : ProjectFilesStyle.documentsList.fileNameHeight * wrapperItem.model.count; + return section.state === "hidden" ? 0 : projectFilesStyle.documentsList.fileNameHeight * wrapperItem.model.count; } function hiddenHeightElement() { - return section.state === "hidden" ? 0 : ProjectFilesStyle.documentsList.fileNameHeight; + return section.state === "hidden" ? 0 : projectFilesStyle.documentsList.fileNameHeight; } function getDocumentIndex(documentId) @@ -68,7 +68,7 @@ Rectangle { anchors.top: parent.top id: rowCol - height: ProjectFilesStyle.documentsList.height + height: projectFilesStyle.documentsList.height Layout.fillWidth: true @@ -88,15 +88,15 @@ Rectangle id: section text: sectionName anchors.left: parent.left - anchors.leftMargin: ProjectFilesStyle.general.leftMargin - color: ProjectFilesStyle.documentsList.sectionColor + anchors.leftMargin: projectFilesStyle.general.leftMargin + color: projectFilesStyle.documentsList.sectionColor font.family: boldFont.name - font.pointSize: ProjectFilesStyle.documentsList.sectionFontSize + font.pointSize: projectFilesStyle.documentsList.sectionFontSize states: [ State { name: "hidden" PropertyChanges { target: filesList; visible: false; } - PropertyChanges { target: rowCol; Layout.minimumHeight: ProjectFilesStyle.documentsList.height; Layout.maximumHeight: ProjectFilesStyle.documentsList.height; height: ProjectFilesStyle.documentsList.height; } + PropertyChanges { target: rowCol; Layout.minimumHeight: projectFilesStyle.documentsList.height; Layout.maximumHeight: projectFilesStyle.documentsList.height; height: projectFilesStyle.documentsList.height; } PropertyChanges { target: imgArrow; source: "qrc:/qml/img/closedtriangleindicator_filesproject.png" } } ] @@ -138,7 +138,7 @@ Rectangle Layout.preferredHeight: wrapperItem.hiddenHeightElement() Layout.maximumHeight: wrapperItem.hiddenHeightElement() height: wrapperItem.hiddenHeightElement() - color: isSelected ? ProjectFilesStyle.documentsList.highlightColor : "transparent" + color: isSelected ? projectFilesStyle.documentsList.highlightColor : "transparent" property bool isSelected property bool renameMode @@ -147,15 +147,15 @@ Rectangle anchors.verticalCenter: parent.verticalCenter anchors.fill: parent anchors.left: parent.left - anchors.leftMargin: ProjectFilesStyle.general.leftMargin + 2 + anchors.leftMargin: projectFilesStyle.general.leftMargin + 2 Text { id: nameText height: parent.height visible: !renameMode - color: rootItem.isSelected ? ProjectFilesStyle.documentsList.selectedColor : ProjectFilesStyle.documentsList.color + color: rootItem.isSelected ? projectFilesStyle.documentsList.selectedColor : projectFilesStyle.documentsList.color text: name; font.family: fileNameFont.name - font.pointSize: ProjectFilesStyle.documentsList.fontSize + font.pointSize: projectFilesStyle.documentsList.fontSize verticalAlignment: Text.AlignVCenter Connections @@ -182,7 +182,7 @@ Rectangle DefaultLabel { id: editStatusLabel visible: false - color: rootItem.isSelected ? ProjectFilesStyle.documentsList.selectedColor : ProjectFilesStyle.documentsList.color + color: rootItem.isSelected ? projectFilesStyle.documentsList.selectedColor : projectFilesStyle.documentsList.color verticalAlignment: Text.AlignVCenter text: "*" width: 10 @@ -196,7 +196,7 @@ Rectangle visible: renameMode anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left - anchors.leftMargin: ProjectFilesStyle.general.leftMargin + anchors.leftMargin: projectFilesStyle.general.leftMargin MouseArea { id: textMouseArea anchors.fill: parent diff --git a/mix/qml/LogsPane.qml b/mix/qml/LogsPane.qml index b40dfc4c7..7351667d4 100644 --- a/mix/qml/LogsPane.qml +++ b/mix/qml/LogsPane.qml @@ -3,7 +3,6 @@ import QtQuick.Layouts 1.0 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.3 import org.ethereum.qml.SortFilterProxyModel 1.0 -import "." Rectangle { @@ -13,11 +12,16 @@ Rectangle logsModel.insert(0, { "type": _type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss dd.MM.yyyy"), "content": _content, "level": _level }); } + LogsPaneStyle { + id: logStyle + } + + id: root anchors.fill: parent radius: 5 - color: LogsPaneStyle.generic.layout.backgroundColor - border.color: LogsPaneStyle.generic.layout.borderColor - border.width: LogsPaneStyle.generic.layout.borderWidth + color: logStyle.generic.layout.backgroundColor + border.color: logStyle.generic.layout.borderColor + border.width: logStyle.generic.layout.borderWidth ColumnLayout { z: 2 height: parent.height @@ -26,14 +30,14 @@ Rectangle Row { id: rowAction - Layout.preferredHeight: LogsPaneStyle.generic.layout.headerHeight - height: LogsPaneStyle.generic.layout.headerHeight - anchors.leftMargin: LogsPaneStyle.generic.layout.leftMargin + Layout.preferredHeight: logStyle.generic.layout.headerHeight + height: logStyle.generic.layout.headerHeight + anchors.leftMargin: logStyle.generic.layout.leftMargin anchors.left: parent.left - spacing: LogsPaneStyle.generic.layout.headerButtonSpacing + spacing: logStyle.generic.layout.headerButtonSpacing Button { - height: LogsPaneStyle.generic.layout.headerButtonHeight + height: logStyle.generic.layout.headerButtonHeight anchors.verticalCenter: parent.verticalCenter action: clearAction iconSource: "qrc:/qml/img/broom.png" @@ -50,7 +54,7 @@ Rectangle Button { - height: LogsPaneStyle.generic.layout.headerButtonHeight + height: logStyle.generic.layout.headerButtonHeight anchors.verticalCenter: parent.verticalCenter action: copytoClipBoardAction iconSource: "qrc:/qml/img/copy.png" @@ -81,7 +85,7 @@ Rectangle ToolButton { id: javascriptButton checkable: true - height: LogsPaneStyle.generic.layout.headerButtonHeight + height: logStyle.generic.layout.headerButtonHeight anchors.verticalCenter: parent.verticalCenter checked: true onCheckedChanged: { @@ -93,9 +97,9 @@ Rectangle label: Item { DefaultLabel { - font.family: LogsPaneStyle.generic.layout.logLabelFont - font.pointSize: Style.absoluteSize(-3) - color: LogsPaneStyle.generic.layout.logLabelColor + font.family: logStyle.generic.layout.logLabelFont + font.pointSize: appStyle.absoluteSize(-3) + color: logStyle.generic.layout.logLabelColor anchors.centerIn: parent text: qsTr("JavaScript") } @@ -106,7 +110,7 @@ Rectangle ToolButton { id: runButton checkable: true - height: LogsPaneStyle.generic.layout.headerButtonHeight + height: logStyle.generic.layout.headerButtonHeight anchors.verticalCenter: parent.verticalCenter checked: true onCheckedChanged: { @@ -118,9 +122,9 @@ Rectangle label: Item { DefaultLabel { - font.family: LogsPaneStyle.generic.layout.logLabelFont - font.pointSize: Style.absoluteSize(-3) - color: LogsPaneStyle.generic.layout.logLabelColor + font.family: logStyle.generic.layout.logLabelFont + font.pointSize: appStyle.absoluteSize(-3) + color: logStyle.generic.layout.logLabelColor anchors.centerIn: parent text: qsTr("Run") } @@ -131,7 +135,7 @@ Rectangle ToolButton { id: stateButton checkable: true - height: LogsPaneStyle.generic.layout.headerButtonHeight + height: logStyle.generic.layout.headerButtonHeight anchors.verticalCenter: parent.verticalCenter checked: true onCheckedChanged: { @@ -143,8 +147,8 @@ Rectangle label: Item { DefaultLabel { - font.family: LogsPaneStyle.generic.layout.logLabelFont - font.pointSize: Style.absoluteSize(-3) + font.family: logStyle.generic.layout.logLabelFont + font.pointSize: appStyle.absoluteSize(-3) color: "#5391d8" anchors.centerIn: parent text: qsTr("State") @@ -156,7 +160,7 @@ Rectangle ToolButton { id: compilationButton checkable: true - height: LogsPaneStyle.generic.layout.headerButtonHeight + height: logStyle.generic.layout.headerButtonHeight anchors.verticalCenter: parent.verticalCenter checked: false onCheckedChanged: { @@ -168,8 +172,8 @@ Rectangle label: Item { DefaultLabel { - font.family: LogsPaneStyle.generic.layout.logLabelFont - font.pointSize: Style.absoluteSize(-3) + font.family: logStyle.generic.layout.logLabelFont + font.pointSize: appStyle.absoluteSize(-3) color: "#5391d8" anchors.centerIn: parent text: qsTr("Compilation") @@ -181,11 +185,11 @@ Rectangle DefaultTextField { id: searchBox - height: LogsPaneStyle.generic.layout.headerButtonHeight + height: logStyle.generic.layout.headerButtonHeight anchors.verticalCenter: parent.verticalCenter - width: LogsPaneStyle.generic.layout.headerInputWidth - font.family: LogsPaneStyle.generic.layout.logLabelFont - font.pointSize: Style.absoluteSize(-3) + width: logStyle.generic.layout.headerInputWidth + font.family: logStyle.generic.layout.logLabelFont + font.pointSize: appStyle.absoluteSize(-3) font.italic: true onTextChanged: { proxyModel.search(text); @@ -255,21 +259,21 @@ Rectangle { role: "date" title: qsTr("date") - width: LogsPaneStyle.generic.layout.dateWidth + width: logStyle.generic.layout.dateWidth delegate: itemDelegate } TableViewColumn { role: "type" title: qsTr("type") - width: LogsPaneStyle.generic.layout.typeWidth + width: logStyle.generic.layout.typeWidth delegate: itemDelegate } TableViewColumn { role: "content" title: qsTr("content") - width: LogsPaneStyle.generic.layout.contentWidth + width: logStyle.generic.layout.contentWidth delegate: itemDelegate } @@ -277,7 +281,7 @@ Rectangle Rectangle { width: logsTable.width - 4 height: 17 - color: styleData.alternate ? "transparent" : LogsPaneStyle.generic.layout.logAlternateColor + color: styleData.alternate ? "transparent" : logStyle.generic.layout.logAlternateColor } } } @@ -286,8 +290,8 @@ Rectangle id: itemDelegate DefaultLabel { text: styleData.value; - font.family: LogsPaneStyle.generic.layout.logLabelFont - font.pointSize: Style.absoluteSize(-1) + font.family: logStyle.generic.layout.logLabelFont + font.pointSize: appStyle.absoluteSize(-1) color: { if (proxyModel.get(styleData.row).level === "error") return "red"; diff --git a/mix/qml/LogsPaneStyle.qml b/mix/qml/LogsPaneStyle.qml index 59b80653a..305e6f16f 100644 --- a/mix/qml/LogsPaneStyle.qml +++ b/mix/qml/LogsPaneStyle.qml @@ -1,4 +1,3 @@ -pragma Singleton import QtQuick 2.0 QtObject { diff --git a/mix/qml/NewProjectDialog.qml b/mix/qml/NewProjectDialog.qml index 1ec53e1d9..3ea2a5a4b 100644 --- a/mix/qml/NewProjectDialog.qml +++ b/mix/qml/NewProjectDialog.qml @@ -15,6 +15,7 @@ Window { property alias projectTitle: titleField.text readonly property string projectPath: "file://" + pathField.text + property alias pathFieldText: pathField.text signal accepted function open() { diff --git a/mix/qml/ProjectFilesStyle.qml b/mix/qml/ProjectFilesStyle.qml index f4b83c728..ca4499196 100644 --- a/mix/qml/ProjectFilesStyle.qml +++ b/mix/qml/ProjectFilesStyle.qml @@ -1,4 +1,3 @@ -pragma Singleton import QtQuick 2.0 QtObject { diff --git a/mix/qml/ProjectList.qml b/mix/qml/ProjectList.qml index 6ca6e6d1f..a98c2587b 100644 --- a/mix/qml/ProjectList.qml +++ b/mix/qml/ProjectList.qml @@ -8,6 +8,11 @@ import "." Item { property bool renameMode: false; + + ProjectFilesStyle { + id: projectFilesStyle + } + ColumnLayout { anchors.fill: parent id: filesCol @@ -20,8 +25,8 @@ Item { Rectangle { - color: ProjectFilesStyle.title.background - height: ProjectFilesStyle.title.height + color: projectFilesStyle.title.background + height: projectFilesStyle.title.height Layout.fillWidth: true Image { id: projectIcon @@ -37,14 +42,14 @@ Item { Text { id: projectTitle - color: ProjectFilesStyle.title.color + color: projectFilesStyle.title.color text: projectModel.projectTitle anchors.verticalCenter: parent.verticalCenter visible: !projectModel.isEmpty; anchors.left: parent.left - anchors.leftMargin: ProjectFilesStyle.general.leftMargin + anchors.leftMargin: projectFilesStyle.general.leftMargin font.family: srcSansProLight.name - font.pointSize: ProjectFilesStyle.title.fontSize + font.pointSize: projectFilesStyle.title.fontSize font.weight: Font.Light } @@ -54,7 +59,7 @@ Item { anchors.right: parent.right anchors.rightMargin: 15 font.family: srcSansProLight.name - font.pointSize: ProjectFilesStyle.title.fontSize + font.pointSize: projectFilesStyle.title.fontSize anchors.verticalCenter: parent.verticalCenter font.weight: Font.Light } @@ -64,14 +69,14 @@ Item { { Layout.fillWidth: true height: 3 - color: ProjectFilesStyle.documentsList.background + color: projectFilesStyle.documentsList.background } Rectangle { Layout.fillWidth: true Layout.fillHeight: true - color: ProjectFilesStyle.documentsList.background + color: projectFilesStyle.documentsList.background ColumnLayout { diff --git a/mix/qml/ProjectModel.qml b/mix/qml/ProjectModel.qml index 7cd1363a6..6c859a9f9 100644 --- a/mix/qml/ProjectModel.qml +++ b/mix/qml/ProjectModel.qml @@ -44,6 +44,7 @@ Item { property var stateListModel: projectStateListModel.model property CodeEditorView codeEditor: null property var unsavedFiles: [] + property alias newProjectDialog: newProjectDialog //interface function saveAll() { ProjectModelCode.saveAll(); } diff --git a/mix/qml/Splitter.qml b/mix/qml/Splitter.qml index 96e823795..012379700 100644 --- a/mix/qml/Splitter.qml +++ b/mix/qml/Splitter.qml @@ -9,6 +9,3 @@ SplitView color: "#cccccc" } } - - - diff --git a/mix/qml/StateDialog.qml b/mix/qml/StateDialog.qml index b229ed433..5b96c4fd4 100644 --- a/mix/qml/StateDialog.qml +++ b/mix/qml/StateDialog.qml @@ -17,7 +17,7 @@ Window { height: 480 title: qsTr("Edit State") visible: false - color: StateDialogStyle.generic.backgroundColor + color: stateDialogStyle.generic.backgroundColor property alias stateTitle: titleField.text property alias isDefault: defaultCheckBox.checked @@ -71,6 +71,10 @@ Window { return item; } + StateDialogStyle { + id: stateDialogStyle + } + ColumnLayout { anchors.fill: parent anchors.margins: 10 diff --git a/mix/qml/StateDialogStyle.qml b/mix/qml/StateDialogStyle.qml index 39214312a..993e6a1c3 100644 --- a/mix/qml/StateDialogStyle.qml +++ b/mix/qml/StateDialogStyle.qml @@ -1,4 +1,3 @@ -pragma Singleton import QtQuick 2.0 QtObject { diff --git a/mix/qml/StatusPane.qml b/mix/qml/StatusPane.qml index 6d4b5e7e1..24e136d8e 100644 --- a/mix/qml/StatusPane.qml +++ b/mix/qml/StatusPane.qml @@ -50,6 +50,10 @@ Rectangle { logPane.push("error", type, text); } + StatusPaneStyle { + id: statusPaneStyle + } + Connections { target: webPreview onJavaScriptMessage: @@ -137,7 +141,7 @@ Rectangle { Text { anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter - font.pointSize: StatusPaneStyle.general.statusFontSize + font.pointSize: statusPaneStyle.general.statusFontSize height: 15 font.family: "sans serif" objectName: "status" diff --git a/mix/qml/StatusPaneStyle.qml b/mix/qml/StatusPaneStyle.qml index e6a1c9910..1eb11b48e 100644 --- a/mix/qml/StatusPaneStyle.qml +++ b/mix/qml/StatusPaneStyle.qml @@ -1,4 +1,3 @@ -pragma Singleton import QtQuick 2.0 QtObject { diff --git a/mix/qml/Style.qml b/mix/qml/Style.qml index c317177a3..422831c78 100644 --- a/mix/qml/Style.qml +++ b/mix/qml/Style.qml @@ -1,4 +1,3 @@ -pragma Singleton import QtQuick 2.0 QtObject { diff --git a/mix/qml/TransactionDialog.qml b/mix/qml/TransactionDialog.qml index 5d98d3dbd..f4de1c0e0 100644 --- a/mix/qml/TransactionDialog.qml +++ b/mix/qml/TransactionDialog.qml @@ -13,7 +13,7 @@ Window { width: 520 height: 500; visible: false - color: StateDialogStyle.generic.backgroundColor + color: transactionDialogStyle.generic.backgroundColor title: qsTr("Edit Transaction") property int transactionIndex property alias gas: gasValueEdit.gasValue; @@ -164,6 +164,10 @@ Window { return item; } + StateDialogStyle { + id: transactionDialogStyle + } + ColumnLayout { anchors.fill: parent anchors.margins: 10 diff --git a/mix/qml/WebCodeEditor.qml b/mix/qml/WebCodeEditor.qml index 7e5945fc7..18a706d17 100644 --- a/mix/qml/WebCodeEditor.qml +++ b/mix/qml/WebCodeEditor.qml @@ -7,12 +7,14 @@ import QtWebEngine.experimental 1.0 import "js/ErrorLocationFormater.js" as ErrorLocationFormater Item { - signal editorTextChanged signal breakpointsChanged + signal editorTextChanged + signal loadComplete property bool isClean: true property string currentText: "" property string currentMode: "" property bool initialized: false + property bool unloaded: false property var currentBreakpoints: []; function setText(text, mode) { @@ -78,7 +80,7 @@ Item { onLoadingChanged: { - if (!loading) { + if (!loading && !unloaded && editorBrowser) { initialized = true; setText(currentText, currentMode); runJavaScript("getTextChanged()", function(result) { }); @@ -87,19 +89,19 @@ Item { if (currentMode === "solidity") { codeModel.onCompilationComplete.connect(function(){ - runJavaScript("compilationComplete()", function(result) { }); + editorBrowser.runJavaScript("compilationComplete()", function(result) { }); }); codeModel.onCompilationError.connect(function(error){ var errorInfo = ErrorLocationFormater.extractErrorInfo(error, false); if (errorInfo.line && errorInfo.column) - runJavaScript("compilationError('" + errorInfo.line + "', '" + errorInfo.column + "', '" + errorInfo.errorDetail + "')", function(result) { }); + editorBrowser.runJavaScript("compilationError('" + errorInfo.line + "', '" + errorInfo.column + "', '" + errorInfo.errorDetail + "')", function(result) { }); else - runJavaScript("compilationComplete()", function(result) { }); + editorBrowser.runJavaScript("compilationComplete()", function(result) { }); }); } parent.changeGeneration(); - + loadComplete(); } } @@ -110,6 +112,8 @@ Item { running: false repeat: true onTriggered: { + if (unloaded) + return; editorBrowser.runJavaScript("getTextChanged()", function(result) { if (result === true) { editorBrowser.runJavaScript("getText()" , function(textValue) { diff --git a/mix/qml/WebPreview.qml b/mix/qml/WebPreview.qml index 8507c9b46..b35bd8324 100644 --- a/mix/qml/WebPreview.qml +++ b/mix/qml/WebPreview.qml @@ -60,6 +60,10 @@ Item { setPreviewUrl(urlInput.text); } + WebPreviewStyle { + id: webPreviewStyle + } + Connections { target: mainApplication onLoaded: { @@ -182,7 +186,7 @@ Item { Rectangle { anchors.leftMargin: 4 - color: WebPreviewStyle.general.headerBackgroundColor + color: webPreviewStyle.general.headerBackgroundColor Layout.preferredWidth: parent.width Layout.preferredHeight: 32 Row { @@ -229,7 +233,7 @@ Item { { width: 1 height: parent.height - 10 - color: WebPreviewStyle.general.separatorColor + color: webPreviewStyle.general.separatorColor anchors.verticalCenter: parent.verticalCenter } @@ -250,7 +254,7 @@ Item { { width: 1 height: parent.height - 10 - color: WebPreviewStyle.general.separatorColor + color: webPreviewStyle.general.separatorColor anchors.verticalCenter: parent.verticalCenter } @@ -284,7 +288,7 @@ Item { { Layout.preferredHeight: 1 Layout.preferredWidth: parent.width - color: WebPreviewStyle.general.separatorColor + color: webPreviewStyle.general.separatorColor } Splitter @@ -354,9 +358,9 @@ Item { id: expressionInput width: parent.width - 15 height: 20 - font.family: WebPreviewStyle.general.fontName + font.family: webPreviewStyle.general.fontName font.italic: true - font.pointSize: Style.absoluteSize(-3) + font.pointSize: appStyle.absoluteSize(-3) anchors.verticalCenter: parent.verticalCenter property var history: [] @@ -416,8 +420,8 @@ Item { id: resultTextArea width: expressionPanel.width wrapMode: Text.Wrap - font.family: WebPreviewStyle.general.fontName - font.pointSize: Style.absoluteSize(-3) + font.family: webPreviewStyle.general.fontName + font.pointSize: appStyle.absoluteSize(-3) backgroundVisible: true style: TextAreaStyle { backgroundColor: "#f0f0f0" diff --git a/mix/qml/WebPreviewStyle.qml b/mix/qml/WebPreviewStyle.qml index 231bbe16c..1fc0c8b99 100644 --- a/mix/qml/WebPreviewStyle.qml +++ b/mix/qml/WebPreviewStyle.qml @@ -1,4 +1,3 @@ -pragma Singleton import QtQuick 2.0 QtObject { diff --git a/mix/qml/qmldir b/mix/qml/qmldir deleted file mode 100644 index 73c117941..000000000 --- a/mix/qml/qmldir +++ /dev/null @@ -1,8 +0,0 @@ -singleton Style 1.0 Style.qml -singleton StateDialogStyle 1.0 StateDialogStyle.qml -singleton ProjectFilesStyle 1.0 ProjectFilesStyle.qml -singleton DebuggerPaneStyle 1.0 DebuggerPaneStyle.qml -singleton StateStyle 1.0 StateStyle.qml -singleton StatusPaneStyle 1.0 StatusPaneStyle.qml -singleton WebPreviewStyle 1.0 WebPreviewStyle.qml -singleton LogsPaneStyle 1.0 LogsPaneStyle.qml diff --git a/mix/test/TestMain.cpp b/mix/test/TestMain.cpp new file mode 100644 index 000000000..f21319004 --- /dev/null +++ b/mix/test/TestMain.cpp @@ -0,0 +1,50 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file main.cpp + * @author Yann yann@ethdev.com + * @date 2014 + * Ethereum IDE client. + */ + +#include +#include +#include +#include +#include "MixApplication.h" +#include "Exceptions.h" +#include "TestService.h" + +using namespace dev::mix; + +int main(int _argc, char* _argv[]) +{ + try + { + MixApplication::initialize(); + qmlRegisterType("org.ethereum.qml.TestService", 1, 0, "TestService"); + + return quick_test_main(_argc, _argv, "mix", _argv[1]); + } + catch (boost::exception const& _e) + { + std::cerr << boost::diagnostic_information(_e); + } + catch (std::exception const& _e) + { + std::cerr << _e.what(); + } +} diff --git a/mix/test/TestMain.qml b/mix/test/TestMain.qml deleted file mode 100644 index 737a2b2a4..000000000 --- a/mix/test/TestMain.qml +++ /dev/null @@ -1,51 +0,0 @@ -import QtQuick 2.2 -import QtTest 1.1 -//import Qt.test.qtestroot 1.0 -import "../qml" - - -TestCase -{ - -Item -{ - id: helper - function findChild(item, childId) { - if (item.children) { - var searchString = "button" - - for (var idx in item.children) { - var currentItem = item.children[idx] - - if (currentItem.id.match("^"+childId) === childId) - return currentItem; - - return findChild(currentItem, childId); - } - } - } -} - - -function test_t1() -{ - waitForRendering(mainApplication.mainContent, 10000); - mainApplication.projectModel.createProject(); - var projectDlg = helper.findChild(mainApplication, "newProjectDialog"); - - if (mainApplication.appService.waitForSignal(mainApplication.codeModel, "compilationComplete()", 5000)) - console.log("compiled"); -} - -function runTest() -{ - waitForRendering(mainApplication.mainContent, 10000); - console.log("runtest"); - mousePress(mainApplication, 10, 10); -} - -Application { - id: mainApplication -} - -} diff --git a/mix/test/TestService.cpp b/mix/test/TestService.cpp new file mode 100644 index 000000000..132fe81c0 --- /dev/null +++ b/mix/test/TestService.cpp @@ -0,0 +1,113 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file TestService.cpp + * @author Arkadiy Paronyan arkadiy@ethdev.com + * @date 2015 + * Ethereum IDE client. + */ + +#include "TestService.h" +#include +#include +#include +#include + +namespace dev +{ +namespace mix +{ + +bool TestService::waitForSignal(QObject* _item, QString _signalName, int _timeout) +{ + QSignalSpy spy(_item, ("2" + _signalName.toStdString()).c_str()); + QMetaObject const* mo = _item->metaObject(); + + QStringList methods; + + for(int i = mo->methodOffset(); i < mo->methodCount(); ++i) { + if (mo->method(i).methodType() == QMetaMethod::Signal) { + methods << QString::fromLatin1(mo->method(i).methodSignature()); + } + } + + QElapsedTimer timer; + timer.start(); + + while (!spy.size()) { + int remaining = _timeout - int(timer.elapsed()); + if (remaining <= 0) + break; + QCoreApplication::processEvents(QEventLoop::AllEvents, remaining); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QTest::qSleep(10); + } + + return spy.size(); +} + +bool TestService::keyPress(int _key, int _modifiers, int _delay) +{ + QWindow *window = eventWindow(); + QTest::keyPress(window, Qt::Key(_key), Qt::KeyboardModifiers(_modifiers), _delay); + return true; +} + +bool TestService::keyRelease(int _key, int _modifiers, int _delay) +{ + QWindow *window = eventWindow(); + QTest::keyRelease(window, Qt::Key(_key), Qt::KeyboardModifiers(_modifiers), _delay); + return true; +} + +bool TestService::keyClick(int _key, int _modifiers, int _delay) +{ + QWindow *window = eventWindow(); + QTest::keyClick(window, Qt::Key(_key), Qt::KeyboardModifiers(_modifiers), _delay); + return true; +} + +bool TestService::keyPressChar(QString const& _character, int _modifiers, int _delay) +{ + QTEST_ASSERT(_character.length() == 1); + QWindow *window = eventWindow(); + QTest::keyPress(window, _character[0].toLatin1(), Qt::KeyboardModifiers(_modifiers), _delay); + return true; +} + +bool TestService::keyReleaseChar(QString const& _character, int _modifiers, int _delay) +{ + QTEST_ASSERT(_character.length() == 1); + QWindow *window = eventWindow(); + QTest::keyRelease(window, _character[0].toLatin1(), Qt::KeyboardModifiers(_modifiers), _delay); + return true; +} + +bool TestService::keyClickChar(QString const& _character, int _modifiers, int _delay) +{ + QTEST_ASSERT(_character.length() == 1); + QWindow *window = eventWindow(); + QTest::keyClick(window, _character[0].toLatin1(), Qt::KeyboardModifiers(_modifiers), _delay); + return true; +} + +QWindow* TestService::eventWindow() +{ + return 0; +} + +} +} diff --git a/mix/test/TestService.h b/mix/test/TestService.h new file mode 100644 index 000000000..0049b2259 --- /dev/null +++ b/mix/test/TestService.h @@ -0,0 +1,52 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file TestService.h + * @author Arkadiy Paronyan arkadiy@ethdev.com + * @date 2015 + * Ethereum IDE client. + */ + +#pragma once + +#include + +class QWindow; + +namespace dev +{ +namespace mix +{ + +class TestService: public QObject +{ + Q_OBJECT + +public slots: + bool waitForSignal(QObject* _item, QString _signalName, int _timeout); + bool keyPress(int _key, int _modifiers, int _delay); + bool keyRelease(int _key, int _modifiers, int _delay); + bool keyClick(int _key, int _modifiers, int _delay); + bool keyPressChar(QString const& _character, int _modifiers, int _delay); + bool keyReleaseChar(QString const& _character, int _modifiers, int _delay); + bool keyClickChar(QString const& _character, int _modifiers, int _delay); + +private: + QWindow* eventWindow(); +}; + +} +} diff --git a/mix/test/qml/TestMain.qml b/mix/test/qml/TestMain.qml new file mode 100644 index 000000000..861d875f5 --- /dev/null +++ b/mix/test/qml/TestMain.qml @@ -0,0 +1,52 @@ +import QtQuick 2.2 +import QtTest 1.1 +import org.ethereum.qml.TestService 1.0 +import "../../qml" + + + +TestCase +{ + id: tc + +TestService +{ + id: ts + function typeString(str) + { + for (var c in str) + ts.keyClickChar(c, Qt.NoModifier, 20); + } +} + + +function test_t1() +{ + waitForRendering(mainApplication.mainContent, 10000); + mainApplication.projectModel.createProject(); + var projectDlg = mainApplication.projectModel.newProjectDialog; + projectDlg.projectTitle = "TestProject"; + projectDlg.pathFieldText = "/tmp/MixTest"; //TODO: get platform temp path + projectDlg.acceptAndClose(); + ts.waitForSignal(mainApplication.mainContent.codeEditor, "loadComplete()", 5000) + + ts.keyClickChar("A", Qt.ControlModifier, 20); + ts.typeString("CCC"); + if (ts.waitForSignal(mainApplication.codeModel, "compilationComplete()", 5000)) + console.log("compiled"); + ts.waitForSignal(mainApplication.codeModel, "compilationComplete()", 5000); +} + +function runTest() +{ + waitForRendering(mainApplication.mainContent, 10000); + console.log("runtest"); + mousePress(mainApplication, 10, 10); +} + +Application +{ + id: mainApplication +} +} + diff --git a/mix/test/TestTransactionDebug.qml b/mix/test/qml/TestTransactionDebug.qml similarity index 100% rename from mix/test/TestTransactionDebug.qml rename to mix/test/qml/TestTransactionDebug.qml diff --git a/mix/web.qrc b/mix/web.qrc index aa5b38a67..18cb92b1c 100644 --- a/mix/web.qrc +++ b/mix/web.qrc @@ -8,7 +8,7 @@ qml/html/cm/codemirror.js qml/html/cm/javascript.js qml/html/cm/matchbrackets.js - qml/WebCodeEditor.qml + qml/WebCodeEditor.qml qml/html/codeeditor.js qml/html/cm/fullscreen.css qml/html/cm/fullscreen.js From b1a51ebd469d2439d3743744fa55ae0477c79978 Mon Sep 17 00:00:00 2001 From: arkpar Date: Mon, 30 Mar 2015 14:29:18 +0200 Subject: [PATCH 05/11] continue work on tests --- mix/qml/CodeEditorView.qml | 1 - mix/qml/MainContent.qml | 1 + mix/qml/WebCodeEditor.qml | 21 +++++--- mix/qml/WebPreview.qml | 1 + mix/test/TestService.cpp | 19 +++++++ mix/test/TestService.h | 6 +++ mix/test/qml/TestMain.qml | 107 ++++++++++++++++++++++++------------- 7 files changed, 111 insertions(+), 45 deletions(-) diff --git a/mix/qml/CodeEditorView.qml b/mix/qml/CodeEditorView.qml index dc8b7a757..87ad95bcd 100644 --- a/mix/qml/CodeEditorView.qml +++ b/mix/qml/CodeEditorView.qml @@ -203,7 +203,6 @@ Item { id: editors model: editorListModel onItemRemoved: { - console.log("unloaded"); item.item.unloaded = true; } delegate: Loader { diff --git a/mix/qml/MainContent.qml b/mix/qml/MainContent.qml index 67034f108..ca08485c3 100644 --- a/mix/qml/MainContent.qml +++ b/mix/qml/MainContent.qml @@ -22,6 +22,7 @@ Rectangle { property alias rightViewVisible: rightView.visible property alias webViewVisible: webPreview.visible + property alias webView: webPreview property alias projectViewVisible: projectList.visible property alias runOnProjectLoad: mainSettings.runOnProjectLoad property alias rightPane: rightView diff --git a/mix/qml/WebCodeEditor.qml b/mix/qml/WebCodeEditor.qml index 2d6bc94eb..c33984d36 100644 --- a/mix/qml/WebCodeEditor.qml +++ b/mix/qml/WebCodeEditor.qml @@ -19,10 +19,11 @@ Item { function setText(text, mode) { currentText = text; - currentMode = mode; + if(mode !== undefined) + currentMode = mode; if (initialized) { editorBrowser.runJavaScript("setTextBase64(\"" + Qt.btoa(text) + "\")"); - editorBrowser.runJavaScript("setMode(\"" + mode + "\")"); + editorBrowser.runJavaScript("setMode(\"" + currentMode + "\")"); } setFocus(); } @@ -94,15 +95,19 @@ Item { if (currentMode === "solidity") { codeModel.onCompilationComplete.connect(function(){ - editorBrowser.runJavaScript("compilationComplete()", function(result) { }); + if (editorBrowser) + editorBrowser.runJavaScript("compilationComplete()", function(result) { }); }); codeModel.onCompilationError.connect(function(error){ - var errorInfo = ErrorLocationFormater.extractErrorInfo(error, false); - if (errorInfo.line && errorInfo.column) - editorBrowser.runJavaScript("compilationError('" + errorInfo.line + "', '" + errorInfo.column + "', '" + errorInfo.errorDetail + "')", function(result) { }); - else - editorBrowser.runJavaScript("compilationComplete()", function(result) { }); + if (editorBrowser) + { + var errorInfo = ErrorLocationFormater.extractErrorInfo(error, false); + if (errorInfo.line && errorInfo.column) + editorBrowser.runJavaScript("compilationError('" + errorInfo.line + "', '" + errorInfo.column + "', '" + errorInfo.errorDetail + "')", function(result) { }); + else + editorBrowser.runJavaScript("compilationComplete()", function(result) { }); + } }); } parent.changeGeneration(); diff --git a/mix/qml/WebPreview.qml b/mix/qml/WebPreview.qml index b35bd8324..0203ac680 100644 --- a/mix/qml/WebPreview.qml +++ b/mix/qml/WebPreview.qml @@ -12,6 +12,7 @@ Item { id: webPreview property string pendingPageUrl: "" property bool initialized: false + property alias urlInput: urlInput signal javaScriptMessage(var _level, string _sourceId, var _lineNb, string _content) function setPreviewUrl(url) { diff --git a/mix/test/TestService.cpp b/mix/test/TestService.cpp index 132fe81c0..c923a1bce 100644 --- a/mix/test/TestService.cpp +++ b/mix/test/TestService.cpp @@ -23,6 +23,8 @@ #include "TestService.h" #include #include +#include +#include #include #include @@ -104,8 +106,25 @@ bool TestService::keyClickChar(QString const& _character, int _modifiers, int _d return true; } +void TestService::setTargetWindow(QObject* _window) +{ + QQuickWindow* window = qobject_cast(_window); + if (window) + m_targetWindow = window; + window->requestActivate(); +} + QWindow* TestService::eventWindow() { + QQuickWindow* window = qobject_cast(m_targetWindow); + if (window) + { + window->requestActivate(); + return window; + } + QQuickItem* item = qobject_cast(m_targetWindow); + if (item) + return item->window(); return 0; } diff --git a/mix/test/TestService.h b/mix/test/TestService.h index 0049b2259..d52fbe0c4 100644 --- a/mix/test/TestService.h +++ b/mix/test/TestService.h @@ -34,6 +34,11 @@ namespace mix class TestService: public QObject { Q_OBJECT + Q_PROPERTY(QObject* targetWindow READ targetWindow WRITE setTargetWindow) + +public: + QObject* targetWindow() const { return m_targetWindow; } + void setTargetWindow(QObject* _window); public slots: bool waitForSignal(QObject* _item, QString _signalName, int _timeout); @@ -46,6 +51,7 @@ public slots: private: QWindow* eventWindow(); + QObject* m_targetWindow; }; } diff --git a/mix/test/qml/TestMain.qml b/mix/test/qml/TestMain.qml index 861d875f5..fd6619c0c 100644 --- a/mix/test/qml/TestMain.qml +++ b/mix/test/qml/TestMain.qml @@ -3,50 +3,85 @@ import QtTest 1.1 import org.ethereum.qml.TestService 1.0 import "../../qml" - - TestCase { id: tc - -TestService -{ - id: ts - function typeString(str) + TestService { - for (var c in str) - ts.keyClickChar(c, Qt.NoModifier, 20); + id: ts + targetWindow: mainApplication + function typeString(str) + { + for (var c in str) + { + ts.keyPressChar(str[c], Qt.NoModifier, 0); + ts.keyReleaseChar(str[c], Qt.NoModifier, 0); + } + } } -} + function newProject() + { + waitForRendering(mainApplication.mainContent, 10000); + mainApplication.projectModel.createProject(); + var projectDlg = mainApplication.projectModel.newProjectDialog; + //if (ts.waitForSignal(projectDlg, "visibleChanged", 1000)) + projectDlg.projectTitle = "TestProject"; + projectDlg.pathFieldText = "/tmp/MixTest"; //TODO: get platform temp path + projectDlg.acceptAndClose(); + ts.waitForSignal(mainApplication.mainContent.codeEditor, "loadComplete()", 5000) + wait(300); + } -function test_t1() -{ - waitForRendering(mainApplication.mainContent, 10000); - mainApplication.projectModel.createProject(); - var projectDlg = mainApplication.projectModel.newProjectDialog; - projectDlg.projectTitle = "TestProject"; - projectDlg.pathFieldText = "/tmp/MixTest"; //TODO: get platform temp path - projectDlg.acceptAndClose(); - ts.waitForSignal(mainApplication.mainContent.codeEditor, "loadComplete()", 5000) - - ts.keyClickChar("A", Qt.ControlModifier, 20); - ts.typeString("CCC"); - if (ts.waitForSignal(mainApplication.codeModel, "compilationComplete()", 5000)) - console.log("compiled"); - ts.waitForSignal(mainApplication.codeModel, "compilationComplete()", 5000); -} + function test_defaultTransactionSequence() + { + newProject(); + mainApplication.mainContent.codeEditor.getEditor("contract.sol").setText( + //ts.typeString( + "contract Contract {\r" + + " function Contract() {\r" + + " uint x = 69;\r" + + " uint y = 5;\r" + + " for (uint i = 0; i < y; ++i) {\r" + + " x += 42;\r" + + " z += x;\r" + + " }\r" + + " }\r" + + " uint z;\r" + + "}\r" + ); + ts.keyPressChar("S", Qt.ControlModifier, 200); //Ctrl+S + if (!ts.waitForSignal(mainApplication.codeModel, "compilationComplete()", 5000)) + fail("not compiled"); + tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 3); + } -function runTest() -{ - waitForRendering(mainApplication.mainContent, 10000); - console.log("runtest"); - mousePress(mainApplication, 10, 10); -} + function test_ConstructorParameters() + { + newProject(); + mainApplication.mainContent.codeEditor.getEditor("contract.sol").setText( + //ts.typeString( + "contract Contract {\r" + + " function Contract(uint256 x) {\r" + + " z = x;\r" + + " }\r" + + " }\r" + + " function getZ() returns(uint256) {\r" + + " return z;\r" + + " }\r" + + " }\r" + + " uint z;\r" + + "}\r" + ); + ts.keyPressChar("S", Qt.ControlModifier, 200); //Ctrl+S + if (!ts.waitForSignal(mainApplication.codeModel, "compilationComplete()", 5000)) + fail("not compiled"); + tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 3); + } -Application -{ - id: mainApplication -} + Application + { + id: mainApplication + } } From 725d19fa6d1d8c07f288ee4d501b0ee18d0c3cd5 Mon Sep 17 00:00:00 2001 From: arkpar Date: Mon, 30 Mar 2015 18:03:52 +0200 Subject: [PATCH 06/11] more tests --- mix/CMakeLists.txt | 4 ++- mix/qml/CodeEditorView.qml | 31 +++++++++++------ mix/qml/ItemDelegateDataDump.qml | 4 +-- mix/qml/ProjectModel.qml | 1 + mix/qml/StateDialog.qml | 2 ++ mix/qml/StateListModel.qml | 1 + mix/test/qml/TestMain.qml | 57 +++++++++++++++++++++++--------- 7 files changed, 72 insertions(+), 28 deletions(-) diff --git a/mix/CMakeLists.txt b/mix/CMakeLists.txt index 8137c8eba..72e77b523 100644 --- a/mix/CMakeLists.txt +++ b/mix/CMakeLists.txt @@ -29,7 +29,9 @@ else() qt5_add_resources(UI_RESOURCES noweb.qrc) endif() -add_definitions(-DQT_QML_DEBUG) +if (CMAKE_BUILD_TYPE EQUAL "DEBUG") + add_definitions(-DQT_QML_DEBUG) +endif() # eth_add_executable is defined in cmake/EthExecutableHelper.cmake eth_add_executable(${EXECUTABLE} diff --git a/mix/qml/CodeEditorView.qml b/mix/qml/CodeEditorView.qml index 2733238b8..7bff03cc0 100644 --- a/mix/qml/CodeEditorView.qml +++ b/mix/qml/CodeEditorView.qml @@ -7,13 +7,14 @@ import QtQuick.Dialogs 1.1 Item { id: codeEditorView property string currentDocumentId: "" + property int openDocCount: 0 signal documentEdit(string documentId) signal breakpointsChanged(string documentId) signal isCleanChanged(var isClean, string documentId) signal loadComplete function getDocumentText(documentId) { - for (var i = 0; i < editorListModel.count; i++) { + for (var i = 0; i < openDocCount; i++) { if (editorListModel.get(i).documentId === documentId) { return editors.itemAt(i).item.getText(); } @@ -22,7 +23,7 @@ Item { } function isDocumentOpen(documentId) { - for (var i = 0; i < editorListModel.count; i++) + for (var i = 0; i < openDocCount; i++) if (editorListModel.get(i).documentId === documentId && editors.itemAt(i).item) return true; @@ -35,11 +36,20 @@ Item { } function loadDocument(document) { - for (var i = 0; i < editorListModel.count; i++) + for (var i = 0; i < openDocCount; i++) if (editorListModel.get(i).documentId === document.documentId) return; //already open - editorListModel.append(document); + if (editorListModel.count <= openDocCount) + editorListModel.append(document); + else + { + editorListModel.set(openDocCount, document); + editors.itemAt(openDocCount).visible = true; + doLoadDocument(editors.itemAt(openDocCount).item, editorListModel.get(openDocCount)) + } + openDocCount++; + } function doLoadDocument(editor, document) { @@ -63,7 +73,7 @@ Item { } function getEditor(documentId) { - for (var i = 0; i < editorListModel.count; i++) + for (var i = 0; i < openDocCount; i++) { if (editorListModel.get(i).documentId === documentId) return editors.itemAt(i).item; @@ -94,7 +104,7 @@ Item { } function editingContract() { - for (var i = 0; i < editorListModel.count; i++) + for (var i = 0; i < openDocCount; i++) if (editorListModel.get(i).documentId === currentDocumentId) return editorListModel.get(i).isContract; return false; @@ -102,7 +112,7 @@ Item { function getBreakpoints() { var bpMap = {}; - for (var i = 0; i < editorListModel.count; i++) { + for (var i = 0; i < openDocCount; i++) { var documentId = editorListModel.get(i).documentId; var editor = editors.itemAt(i).item; if (editor) { @@ -133,7 +143,7 @@ Item { } onProjectSaving: { - for (var i = 0; i < editorListModel.count; i++) + for (var i = 0; i < openDocCount; i++) { var doc = editorListModel.get(i); var editor = editors.itemAt(i).item; @@ -145,7 +155,7 @@ Item { onProjectSaved: { if (projectModel.appIsClosing || projectModel.projectIsClosing) return; - for (var i = 0; i < editorListModel.count; i++) + for (var i = 0; i < openDocCount; i++) { var doc = editorListModel.get(i); resetEditStatus(doc.documentId); @@ -155,8 +165,9 @@ Item { onProjectClosed: { for (var i = 0; i < editorListModel.count; i++) editors.itemAt(i).visible = false; - editorListModel.clear(); + //editorListModel.clear(); currentDocumentId = ""; + openDocCount = 0; } onDocumentSaved: { diff --git a/mix/qml/ItemDelegateDataDump.qml b/mix/qml/ItemDelegateDataDump.qml index 9aa6d00db..2c1419e61 100644 --- a/mix/qml/ItemDelegateDataDump.qml +++ b/mix/qml/ItemDelegateDataDump.qml @@ -28,7 +28,7 @@ Rectangle { font.bold: true color: "#4a4a4a" text: modelData[0] - font.pointSize: DebuggerPaneStyle.general.dataDumpFontSize; + font.pointSize: dbgStyle.general.dataDumpFontSize; } } @@ -47,7 +47,7 @@ Rectangle { anchors.leftMargin: 4 color: "#4a4a4a" text: modelData[1] - font.pointSize: DebuggerPaneStyle.general.dataDumpFontSize + font.pointSize: dbgStyle.general.dataDumpFontSize } } } diff --git a/mix/qml/ProjectModel.qml b/mix/qml/ProjectModel.qml index d7a07eab2..e27bb89ab 100644 --- a/mix/qml/ProjectModel.qml +++ b/mix/qml/ProjectModel.qml @@ -43,6 +43,7 @@ Item { property string deploymentDir property var listModel: projectListModel property var stateListModel: projectStateListModel.model + property alias stateDialog: projectStateListModel.stateDialog property CodeEditorView codeEditor: null property var unsavedFiles: [] property alias newProjectDialog: newProjectDialog diff --git a/mix/qml/StateDialog.qml b/mix/qml/StateDialog.qml index 5b96c4fd4..f4cb9eb99 100644 --- a/mix/qml/StateDialog.qml +++ b/mix/qml/StateDialog.qml @@ -21,6 +21,8 @@ Window { property alias stateTitle: titleField.text property alias isDefault: defaultCheckBox.checked + property alias model: transactionsModel + property alias transactionDialog: transactionDialog property int stateIndex property var stateTransactions: [] property var stateAccounts: [] diff --git a/mix/qml/StateListModel.qml b/mix/qml/StateListModel.qml index 682c4b52c..c90692572 100644 --- a/mix/qml/StateListModel.qml +++ b/mix/qml/StateListModel.qml @@ -12,6 +12,7 @@ Item { property alias model: stateListModel property var stateList: [] + property alias stateDialog: stateDialog property string defaultAccount: "cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074" //support for old project function fromPlainStateItem(s) { diff --git a/mix/test/qml/TestMain.qml b/mix/test/qml/TestMain.qml index fd6619c0c..20f5e4c1f 100644 --- a/mix/test/qml/TestMain.qml +++ b/mix/test/qml/TestMain.qml @@ -25,7 +25,6 @@ TestCase waitForRendering(mainApplication.mainContent, 10000); mainApplication.projectModel.createProject(); var projectDlg = mainApplication.projectModel.newProjectDialog; - //if (ts.waitForSignal(projectDlg, "visibleChanged", 1000)) projectDlg.projectTitle = "TestProject"; projectDlg.pathFieldText = "/tmp/MixTest"; //TODO: get platform temp path projectDlg.acceptAndClose(); @@ -33,11 +32,23 @@ TestCase wait(300); } + function editContract(c) + { + mainApplication.mainContent.codeEditor.getEditor("contract.sol").setText(c); + ts.keyPressChar("S", Qt.ControlModifier, 200); //Ctrl+S + if (!ts.waitForSignal(mainApplication.codeModel, "compilationComplete()", 5000)) + fail("not compiled"); + } + + function clickElemet(el) + { + mouseClick(el, 0, 0, Qt.LeftButton, Qt.NoModifier, 10) + } + function test_defaultTransactionSequence() { newProject(); - mainApplication.mainContent.codeEditor.getEditor("contract.sol").setText( - //ts.typeString( + editContract( "contract Contract {\r" + " function Contract() {\r" + " uint x = 69;\r" + @@ -50,33 +61,49 @@ TestCase " uint z;\r" + "}\r" ); - ts.keyPressChar("S", Qt.ControlModifier, 200); //Ctrl+S - if (!ts.waitForSignal(mainApplication.codeModel, "compilationComplete()", 5000)) - fail("not compiled"); tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 3); } - function test_ConstructorParameters() + function test_transactionWithParameter() { newProject(); - mainApplication.mainContent.codeEditor.getEditor("contract.sol").setText( - //ts.typeString( + editContract( "contract Contract {\r" + " function Contract(uint256 x) {\r" + " z = x;\r" + - " }\r" + " }\r" + " function getZ() returns(uint256) {\r" + " return z;\r" + - " }\r" + " }\r" + " uint z;\r" + "}\r" ); - ts.keyPressChar("S", Qt.ControlModifier, 200); //Ctrl+S - if (!ts.waitForSignal(mainApplication.codeModel, "compilationComplete()", 5000)) - fail("not compiled"); - tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 3); + mainApplication.projectModel.stateListModel.editState(0); + mainApplication.projectModel.stateDialog.model.editTransaction(2); + //TODO: edit transaction params and check results + } + + function test_constructorParameters() + { + newProject(); + editContract( + "contract Contract {\r" + + " function setZ(uint256 x) {\r" + + " z = x;\r" + + " }\r" + + " function getZ() returns(uint256) {\r" + + " return z;\r" + + " }\r" + + " uint z;\r" + + "}\r" + ); + mainApplication.projectModel.stateListModel.editState(0); + mainApplication.projectModel.stateDialog.model.addTransaction(); + var transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog; + transactionDialog.functionId = "setZ"; + + + //TODO: edit transaction params and check results } Application From 5c31f4cfc40662ad1a0b14792e585191c508411f Mon Sep 17 00:00:00 2001 From: arkpar Date: Mon, 30 Mar 2015 21:00:16 +0200 Subject: [PATCH 07/11] more test improvements --- mix/qml/StateDialog.qml | 8 ++- mix/qml/TransactionDialog.qml | 39 ++++++++------ mix/qml/WebCodeEditor.qml | 6 +++ mix/test/TestService.cpp | 98 ++++++++++++++++++++++++++++------- mix/test/TestService.h | 15 +++--- mix/test/qml/TestMain.qml | 62 ++++++++++++++++------ 6 files changed, 169 insertions(+), 59 deletions(-) diff --git a/mix/qml/StateDialog.qml b/mix/qml/StateDialog.qml index f4cb9eb99..9dc1549c9 100644 --- a/mix/qml/StateDialog.qml +++ b/mix/qml/StateDialog.qml @@ -58,6 +58,11 @@ Window { forceActiveFocus(); } + function acceptAndClose() { + close(); + accepted(); + } + function close() { visible = false; } @@ -298,8 +303,7 @@ Window { Button { text: qsTr("OK"); onClicked: { - close(); - accepted(); + acceptAndClose(); } } Button { diff --git a/mix/qml/TransactionDialog.qml b/mix/qml/TransactionDialog.qml index 820f8d631..62a799b26 100644 --- a/mix/qml/TransactionDialog.qml +++ b/mix/qml/TransactionDialog.qml @@ -59,16 +59,7 @@ Window { contractComboBox.currentIndex = contractIndex; loadFunctions(contractComboBox.currentValue()); - - var functionIndex = -1; - for (var f = 0; f < functionsModel.count; f++) - if (functionsModel.get(f).text === item.functionId) - functionIndex = f; - - if (functionIndex == -1 && functionsModel.count > 0) - functionIndex = 0; //@todo suggest unused function - - functionComboBox.currentIndex = functionIndex; + selectFunction(functionId); paramsModel = []; if (functionId !== contractComboBox.currentValue()) @@ -87,9 +78,6 @@ Window { visible = true; valueField.focus = true; - modalTransactionDialog.height = (paramsModel.length > 0 ? 500 : 300); - paramLabel.visible = paramsModel.length > 0; - paramScroll.visible = paramsModel.length > 0; } function loadFunctions(contractId) @@ -107,6 +95,19 @@ Window { } + function selectFunction(functionId) + { + var functionIndex = -1; + for (var f = 0; f < functionsModel.count; f++) + if (functionsModel.get(f).text === functionId) + functionIndex = f; + + if (functionIndex == -1 && functionsModel.count > 0) + functionIndex = 0; //@todo suggest unused function + + functionComboBox.currentIndex = functionIndex; + } + function loadParameter(parameter) { var type = parameter.type; @@ -136,6 +137,15 @@ Window { typeLoader.members = [] typeLoader.value = paramValues; typeLoader.members = paramsModel; + paramLabel.visible = paramsModel.length > 0; + paramScroll.visible = paramsModel.length > 0; + modalTransactionDialog.height = (paramsModel.length > 0 ? 500 : 300); + } + + function acceptAndClose() + { + close(); + accepted(); } function close() @@ -369,8 +379,7 @@ Window { Button { text: qsTr("OK"); onClicked: { - close(); - accepted(); + acceptAndClose(); } } Button { diff --git a/mix/qml/WebCodeEditor.qml b/mix/qml/WebCodeEditor.qml index a5f68ad66..6ab0b398a 100644 --- a/mix/qml/WebCodeEditor.qml +++ b/mix/qml/WebCodeEditor.qml @@ -4,6 +4,7 @@ import QtQuick.Layouts 1.0 import QtQuick.Controls.Styles 1.1 import QtWebEngine 1.0 import QtWebEngine.experimental 1.0 +import org.ethereum.qml.Clipboard 1.0 import "js/ErrorLocationFormater.js" as ErrorLocationFormater Item { @@ -68,6 +69,11 @@ Item { editorBrowser.runJavaScript("changeGeneration()", function(result) {}); } + Clipboard + { + id: clipboard + } + Connections { target: clipboard onClipboardChanged: syncClipboard() diff --git a/mix/test/TestService.cpp b/mix/test/TestService.cpp index c923a1bce..373630bf7 100644 --- a/mix/test/TestService.cpp +++ b/mix/test/TestService.cpp @@ -33,6 +33,56 @@ namespace dev namespace mix { +enum MouseAction { MousePress, MouseRelease, MouseClick, MouseDoubleClick, MouseMove }; + +static void mouseEvent(MouseAction _action, QWindow* _window, QObject* _item, Qt::MouseButton _button, Qt::KeyboardModifiers _stateKey, QPointF _pos, int _delay=-1) +{ + if (_delay == -1 || _delay < 30) + _delay = 30; + if (_delay > 0) + QTest::qWait(_delay); + + if (_action == MouseClick) { + mouseEvent(MousePress, _window, _item, _button, _stateKey, _pos); + mouseEvent(MouseRelease, _window, _item, _button, _stateKey, _pos); + return; + } + + QPoint pos = _pos.toPoint(); + QQuickItem *sgitem = qobject_cast(_item); + if (sgitem) + pos = sgitem->mapToScene(_pos).toPoint(); + + _stateKey &= static_cast(Qt::KeyboardModifierMask); + + QMouseEvent me(QEvent::User, QPoint(), Qt::LeftButton, _button, _stateKey); + switch (_action) + { + case MousePress: + me = QMouseEvent(QEvent::MouseButtonPress, pos, _window->mapToGlobal(pos), _button, _button, _stateKey); + break; + case MouseRelease: + me = QMouseEvent(QEvent::MouseButtonRelease, pos, _window->mapToGlobal(pos), _button, 0, _stateKey); + break; + case MouseDoubleClick: + me = QMouseEvent(QEvent::MouseButtonDblClick, pos, _window->mapToGlobal(pos), _button, _button, _stateKey); + break; + case MouseMove: + // with move event the _button is NoButton, but 'buttons' holds the currently pressed buttons + me = QMouseEvent(QEvent::MouseMove, pos, _window->mapToGlobal(pos), Qt::NoButton, _button, _stateKey); + break; + default: + break; + } + QSpontaneKeyEvent::setSpontaneous(&me); + if (!qApp->notify(_window, &me)) { + static const char *mouseActionNames[] = + { "MousePress", "MouseRelease", "MouseClick", "MouseDoubleClick", "MouseMove" }; + QString warning = QString::fromLatin1("Mouse event \"%1\" not accepted by receiving window"); + QWARN(warning.arg(QString::fromLatin1(mouseActionNames[static_cast(_action)])).toLatin1().data()); + } +} + bool TestService::waitForSignal(QObject* _item, QString _signalName, int _timeout) { QSignalSpy spy(_item, ("2" + _signalName.toStdString()).c_str()); @@ -61,51 +111,57 @@ bool TestService::waitForSignal(QObject* _item, QString _signalName, int _timeou return spy.size(); } -bool TestService::keyPress(int _key, int _modifiers, int _delay) +bool TestService::keyPress(QObject* _item, int _key, int _modifiers, int _delay) { - QWindow *window = eventWindow(); + QWindow *window = eventWindow(_item); QTest::keyPress(window, Qt::Key(_key), Qt::KeyboardModifiers(_modifiers), _delay); return true; } -bool TestService::keyRelease(int _key, int _modifiers, int _delay) +bool TestService::keyRelease(QObject* _item, int _key, int _modifiers, int _delay) { - QWindow *window = eventWindow(); + QWindow *window = eventWindow(_item); QTest::keyRelease(window, Qt::Key(_key), Qt::KeyboardModifiers(_modifiers), _delay); return true; } -bool TestService::keyClick(int _key, int _modifiers, int _delay) +bool TestService::keyClick(QObject* _item, int _key, int _modifiers, int _delay) { - QWindow *window = eventWindow(); + QWindow *window = eventWindow(_item); QTest::keyClick(window, Qt::Key(_key), Qt::KeyboardModifiers(_modifiers), _delay); return true; } -bool TestService::keyPressChar(QString const& _character, int _modifiers, int _delay) +bool TestService::keyPressChar(QObject* _item, QString const& _character, int _modifiers, int _delay) { - QTEST_ASSERT(_character.length() == 1); - QWindow *window = eventWindow(); + QWindow *window = eventWindow(_item); QTest::keyPress(window, _character[0].toLatin1(), Qt::KeyboardModifiers(_modifiers), _delay); return true; } -bool TestService::keyReleaseChar(QString const& _character, int _modifiers, int _delay) +bool TestService::keyReleaseChar(QObject* _item, QString const& _character, int _modifiers, int _delay) { - QTEST_ASSERT(_character.length() == 1); - QWindow *window = eventWindow(); + QWindow *window = eventWindow(_item); QTest::keyRelease(window, _character[0].toLatin1(), Qt::KeyboardModifiers(_modifiers), _delay); return true; } -bool TestService::keyClickChar(QString const& _character, int _modifiers, int _delay) +bool TestService::keyClickChar(QObject* _item, QString const& _character, int _modifiers, int _delay) { - QTEST_ASSERT(_character.length() == 1); - QWindow *window = eventWindow(); + QWindow *window = eventWindow(_item); QTest::keyClick(window, _character[0].toLatin1(), Qt::KeyboardModifiers(_modifiers), _delay); return true; } +bool TestService::mouseClick(QObject* _item, qreal _x, qreal _y, int _button, int _modifiers, int _delay) +{ + QWindow* window = qobject_cast(_item); + if (!window) + window = eventWindow(_item); + mouseEvent(MouseClick, window, _item, Qt::MouseButton(_button), Qt::KeyboardModifiers(_modifiers), QPointF(_x, _y), _delay); + return true; +} + void TestService::setTargetWindow(QObject* _window) { QQuickWindow* window = qobject_cast(_window); @@ -114,15 +170,21 @@ void TestService::setTargetWindow(QObject* _window) window->requestActivate(); } -QWindow* TestService::eventWindow() +QWindow* TestService::eventWindow(QObject* _item) { - QQuickWindow* window = qobject_cast(m_targetWindow); + QQuickItem* item = qobject_cast(_item); + if (item && item->window()) + return item->window(); + + QQuickWindow* window = qobject_cast(_item); + if (!window) + window = qobject_cast(m_targetWindow); if (window) { window->requestActivate(); return window; } - QQuickItem* item = qobject_cast(m_targetWindow); + item = qobject_cast(m_targetWindow); if (item) return item->window(); return 0; diff --git a/mix/test/TestService.h b/mix/test/TestService.h index d52fbe0c4..fbf6533e6 100644 --- a/mix/test/TestService.h +++ b/mix/test/TestService.h @@ -42,15 +42,16 @@ public: public slots: bool waitForSignal(QObject* _item, QString _signalName, int _timeout); - bool keyPress(int _key, int _modifiers, int _delay); - bool keyRelease(int _key, int _modifiers, int _delay); - bool keyClick(int _key, int _modifiers, int _delay); - bool keyPressChar(QString const& _character, int _modifiers, int _delay); - bool keyReleaseChar(QString const& _character, int _modifiers, int _delay); - bool keyClickChar(QString const& _character, int _modifiers, int _delay); + bool keyPress(QObject* _item, int _key, int _modifiers, int _delay); + bool keyRelease(QObject* _item, int _key, int _modifiers, int _delay); + bool keyClick(QObject* _item, int _key, int _modifiers, int _delay); + bool keyPressChar(QObject* _item, QString const& _character, int _modifiers, int _delay); + bool keyReleaseChar(QObject* _item, QString const& _character, int _modifiers, int _delay); + bool keyClickChar(QObject* _item, QString const& _character, int _modifiers, int _delay); + bool mouseClick(QObject* _item, qreal _x, qreal _y, int _button, int _modifiers, int _delay); private: - QWindow* eventWindow(); + QWindow* eventWindow(QObject* _item); QObject* m_targetWindow; }; diff --git a/mix/test/qml/TestMain.qml b/mix/test/qml/TestMain.qml index 20f5e4c1f..65b5a5076 100644 --- a/mix/test/qml/TestMain.qml +++ b/mix/test/qml/TestMain.qml @@ -10,12 +10,15 @@ TestCase { id: ts targetWindow: mainApplication - function typeString(str) + function typeString(str, el) { + if (el === undefined) + el = mainApplication; + for (var c in str) { - ts.keyPressChar(str[c], Qt.NoModifier, 0); - ts.keyReleaseChar(str[c], Qt.NoModifier, 0); + ts.keyPressChar(el, str[c], Qt.NoModifier, 0); + ts.keyReleaseChar(el, str[c], Qt.NoModifier, 0); } } } @@ -25,24 +28,24 @@ TestCase waitForRendering(mainApplication.mainContent, 10000); mainApplication.projectModel.createProject(); var projectDlg = mainApplication.projectModel.newProjectDialog; + wait(30); projectDlg.projectTitle = "TestProject"; projectDlg.pathFieldText = "/tmp/MixTest"; //TODO: get platform temp path projectDlg.acceptAndClose(); - ts.waitForSignal(mainApplication.mainContent.codeEditor, "loadComplete()", 5000) - wait(300); + wait(30); } function editContract(c) { mainApplication.mainContent.codeEditor.getEditor("contract.sol").setText(c); - ts.keyPressChar("S", Qt.ControlModifier, 200); //Ctrl+S + ts.keyPressChar(mainApplication, "S", Qt.ControlModifier, 200); //Ctrl+S if (!ts.waitForSignal(mainApplication.codeModel, "compilationComplete()", 5000)) fail("not compiled"); } - function clickElemet(el) + function clickElement(el, x, y) { - mouseClick(el, 0, 0, Qt.LeftButton, Qt.NoModifier, 10) + ts.mouseClick(el, x, y, Qt.LeftButton, Qt.NoModifier, 10) } function test_defaultTransactionSequence() @@ -61,6 +64,8 @@ TestCase " uint z;\r" + "}\r" ); + if (!ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000)) + fail("not run"); tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 3); } @@ -69,7 +74,7 @@ TestCase newProject(); editContract( "contract Contract {\r" + - " function Contract(uint256 x) {\r" + + " function setZ(uint256 x) {\r" + " z = x;\r" + " }\r" + " function getZ() returns(uint256) {\r" + @@ -79,8 +84,22 @@ TestCase "}\r" ); mainApplication.projectModel.stateListModel.editState(0); - mainApplication.projectModel.stateDialog.model.editTransaction(2); - //TODO: edit transaction params and check results + mainApplication.projectModel.stateDialog.model.addTransaction(); + var transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog; + transactionDialog.selectFunction("setZ"); + clickElement(transactionDialog, 140, 300); + ts.typeString("442", transactionDialog); + transactionDialog.acceptAndClose(); + mainApplication.projectModel.stateDialog.model.addTransaction(); + transactionDialog.selectFunction("getZ"); + transactionDialog.acceptAndClose(); + mainApplication.projectModel.stateDialog.acceptAndClose(); + mainApplication.mainContent.startQuickDebugging(); + wait(1); + if (!ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000)) + fail("not run"); + tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 5); + tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(4), "returned", "(442)"); } function test_constructorParameters() @@ -88,7 +107,7 @@ TestCase newProject(); editContract( "contract Contract {\r" + - " function setZ(uint256 x) {\r" + + " function Contract(uint256 x) {\r" + " z = x;\r" + " }\r" + " function getZ() returns(uint256) {\r" + @@ -98,12 +117,21 @@ TestCase "}\r" ); mainApplication.projectModel.stateListModel.editState(0); - mainApplication.projectModel.stateDialog.model.addTransaction(); + mainApplication.projectModel.stateDialog.model.editTransaction(2); var transactionDialog = mainApplication.projectModel.stateDialog.transactionDialog; - transactionDialog.functionId = "setZ"; - - - //TODO: edit transaction params and check results + clickElement(transactionDialog, 140, 300); + ts.typeString("442", transactionDialog); + transactionDialog.acceptAndClose(); + mainApplication.projectModel.stateDialog.model.addTransaction(); + transactionDialog.selectFunction("getZ"); + transactionDialog.acceptAndClose(); + mainApplication.projectModel.stateDialog.acceptAndClose(); + wait(1); + mainApplication.mainContent.startQuickDebugging(); + if (!ts.waitForSignal(mainApplication.clientModel, "runComplete()", 5000)) + fail("not run"); + tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel, "count", 4); + tryCompare(mainApplication.mainContent.rightPane.transactionLog.transactionModel.get(3), "returned", "(442)"); } Application From 34e0f03e57e520e41e12098910e6eced7ac3414f Mon Sep 17 00:00:00 2001 From: arkpar Date: Tue, 31 Mar 2015 11:46:51 +0200 Subject: [PATCH 08/11] styled with astyle --- mix/QVariableDefinition.cpp | 1 + mix/test/TestService.cpp | 68 +++++++++++++++--------------- mix/web.qrc | 82 ++++++++++++++++++------------------- 3 files changed, 76 insertions(+), 75 deletions(-) diff --git a/mix/QVariableDefinition.cpp b/mix/QVariableDefinition.cpp index 4df31ea92..4553ffad6 100644 --- a/mix/QVariableDefinition.cpp +++ b/mix/QVariableDefinition.cpp @@ -26,6 +26,7 @@ namespace dev { namespace mix { + QString QVariableDefinition::encodeValueAsString() { return QString::fromStdString(dev::toHex(encodeValue())); diff --git a/mix/test/TestService.cpp b/mix/test/TestService.cpp index 373630bf7..7ad2bc13d 100644 --- a/mix/test/TestService.cpp +++ b/mix/test/TestService.cpp @@ -35,21 +35,22 @@ namespace mix enum MouseAction { MousePress, MouseRelease, MouseClick, MouseDoubleClick, MouseMove }; -static void mouseEvent(MouseAction _action, QWindow* _window, QObject* _item, Qt::MouseButton _button, Qt::KeyboardModifiers _stateKey, QPointF _pos, int _delay=-1) +static void mouseEvent(MouseAction _action, QWindow* _window, QObject* _item, Qt::MouseButton _button, Qt::KeyboardModifiers _stateKey, QPointF _pos, int _delay = -1) { if (_delay == -1 || _delay < 30) _delay = 30; if (_delay > 0) QTest::qWait(_delay); - if (_action == MouseClick) { + if (_action == MouseClick) + { mouseEvent(MousePress, _window, _item, _button, _stateKey, _pos); mouseEvent(MouseRelease, _window, _item, _button, _stateKey, _pos); return; } QPoint pos = _pos.toPoint(); - QQuickItem *sgitem = qobject_cast(_item); + QQuickItem* sgitem = qobject_cast(_item); if (sgitem) pos = sgitem->mapToScene(_pos).toPoint(); @@ -58,26 +59,26 @@ static void mouseEvent(MouseAction _action, QWindow* _window, QObject* _item, Qt QMouseEvent me(QEvent::User, QPoint(), Qt::LeftButton, _button, _stateKey); switch (_action) { - case MousePress: - me = QMouseEvent(QEvent::MouseButtonPress, pos, _window->mapToGlobal(pos), _button, _button, _stateKey); - break; - case MouseRelease: - me = QMouseEvent(QEvent::MouseButtonRelease, pos, _window->mapToGlobal(pos), _button, 0, _stateKey); - break; - case MouseDoubleClick: - me = QMouseEvent(QEvent::MouseButtonDblClick, pos, _window->mapToGlobal(pos), _button, _button, _stateKey); - break; - case MouseMove: - // with move event the _button is NoButton, but 'buttons' holds the currently pressed buttons - me = QMouseEvent(QEvent::MouseMove, pos, _window->mapToGlobal(pos), Qt::NoButton, _button, _stateKey); - break; - default: - break; + case MousePress: + me = QMouseEvent(QEvent::MouseButtonPress, pos, _window->mapToGlobal(pos), _button, _button, _stateKey); + break; + case MouseRelease: + me = QMouseEvent(QEvent::MouseButtonRelease, pos, _window->mapToGlobal(pos), _button, 0, _stateKey); + break; + case MouseDoubleClick: + me = QMouseEvent(QEvent::MouseButtonDblClick, pos, _window->mapToGlobal(pos), _button, _button, _stateKey); + break; + case MouseMove: + // with move event the _button is NoButton, but 'buttons' holds the currently pressed buttons + me = QMouseEvent(QEvent::MouseMove, pos, _window->mapToGlobal(pos), Qt::NoButton, _button, _stateKey); + break; + default: + break; } QSpontaneKeyEvent::setSpontaneous(&me); - if (!qApp->notify(_window, &me)) { - static const char *mouseActionNames[] = - { "MousePress", "MouseRelease", "MouseClick", "MouseDoubleClick", "MouseMove" }; + if (!qApp->notify(_window, &me)) + { + static const char* mouseActionNames[] = { "MousePress", "MouseRelease", "MouseClick", "MouseDoubleClick", "MouseMove" }; QString warning = QString::fromLatin1("Mouse event \"%1\" not accepted by receiving window"); QWARN(warning.arg(QString::fromLatin1(mouseActionNames[static_cast(_action)])).toLatin1().data()); } @@ -85,21 +86,20 @@ static void mouseEvent(MouseAction _action, QWindow* _window, QObject* _item, Qt bool TestService::waitForSignal(QObject* _item, QString _signalName, int _timeout) { - QSignalSpy spy(_item, ("2" + _signalName.toStdString()).c_str()); + QSignalSpy spy(_item, ("2" + _signalName.toStdString()).c_str()); QMetaObject const* mo = _item->metaObject(); QStringList methods; - for(int i = mo->methodOffset(); i < mo->methodCount(); ++i) { - if (mo->method(i).methodType() == QMetaMethod::Signal) { + for (int i = mo->methodOffset(); i < mo->methodCount(); ++i) + if (mo->method(i).methodType() == QMetaMethod::Signal) methods << QString::fromLatin1(mo->method(i).methodSignature()); - } - } QElapsedTimer timer; timer.start(); - while (!spy.size()) { + while (!spy.size()) + { int remaining = _timeout - int(timer.elapsed()); if (remaining <= 0) break; @@ -113,42 +113,42 @@ bool TestService::waitForSignal(QObject* _item, QString _signalName, int _timeou bool TestService::keyPress(QObject* _item, int _key, int _modifiers, int _delay) { - QWindow *window = eventWindow(_item); + QWindow* window = eventWindow(_item); QTest::keyPress(window, Qt::Key(_key), Qt::KeyboardModifiers(_modifiers), _delay); return true; } bool TestService::keyRelease(QObject* _item, int _key, int _modifiers, int _delay) { - QWindow *window = eventWindow(_item); + QWindow* window = eventWindow(_item); QTest::keyRelease(window, Qt::Key(_key), Qt::KeyboardModifiers(_modifiers), _delay); return true; } bool TestService::keyClick(QObject* _item, int _key, int _modifiers, int _delay) { - QWindow *window = eventWindow(_item); + QWindow* window = eventWindow(_item); QTest::keyClick(window, Qt::Key(_key), Qt::KeyboardModifiers(_modifiers), _delay); return true; } bool TestService::keyPressChar(QObject* _item, QString const& _character, int _modifiers, int _delay) { - QWindow *window = eventWindow(_item); + QWindow* window = eventWindow(_item); QTest::keyPress(window, _character[0].toLatin1(), Qt::KeyboardModifiers(_modifiers), _delay); return true; } bool TestService::keyReleaseChar(QObject* _item, QString const& _character, int _modifiers, int _delay) { - QWindow *window = eventWindow(_item); + QWindow* window = eventWindow(_item); QTest::keyRelease(window, _character[0].toLatin1(), Qt::KeyboardModifiers(_modifiers), _delay); return true; } bool TestService::keyClickChar(QObject* _item, QString const& _character, int _modifiers, int _delay) { - QWindow *window = eventWindow(_item); + QWindow* window = eventWindow(_item); QTest::keyClick(window, _character[0].toLatin1(), Qt::KeyboardModifiers(_modifiers), _delay); return true; } @@ -172,7 +172,7 @@ void TestService::setTargetWindow(QObject* _window) QWindow* TestService::eventWindow(QObject* _item) { - QQuickItem* item = qobject_cast(_item); + QQuickItem* item = qobject_cast(_item); if (item && item->window()) return item->window(); diff --git a/mix/web.qrc b/mix/web.qrc index 3844f7c72..6870411c5 100644 --- a/mix/web.qrc +++ b/mix/web.qrc @@ -1,44 +1,44 @@ - - qml/WebPreview.qml - qml/html/WebContainer.html - qml/html/cm/active-line.js - qml/html/codeeditor.html - qml/html/cm/codemirror.css - qml/html/cm/codemirror.js - qml/html/cm/javascript.js - qml/html/cm/matchbrackets.js + qml/WebCodeEditor.qml - qml/html/codeeditor.js - qml/html/cm/fullscreen.css - qml/html/cm/fullscreen.js - qml/html/cm/solarized.css - qml/html/cm/xml.js - qml/html/cm/htmlmixed.js - qml/html/cm/css.js - qml/html/cm/solidity.js - qml/html/cm/dialog.css - qml/html/cm/dialog.js - qml/html/cm/search.js - qml/html/cm/searchcursor.js - qml/html/cm/anyword-hint.js - qml/html/cm/show-hint.js - qml/html/cm/show-hint.css - qml/html/cm/closebrackets.js - qml/html/cm/solidityToken.js - qml/html/cm/javascript-hint.js - qml/html/cm/errorannotation.js - qml/html/cm/tern.js - qml/html/cm/ecma5spec.js - qml/html/cm/comment.js - qml/html/cm/def.js - qml/html/cm/doc_comment.js - qml/html/cm/infer.js - qml/html/cm/signal.js - qml/html/cm/ternserver.js - qml/html/cm/acorn.js - qml/html/cm/acorn_loose.js - qml/html/cm/walk.js - qml/html/cm/mark-selection.js - + qml/WebPreview.qml + qml/html/WebContainer.html + qml/html/cm/acorn.js + qml/html/cm/acorn_loose.js + qml/html/cm/active-line.js + qml/html/cm/anyword-hint.js + qml/html/cm/closebrackets.js + qml/html/cm/codemirror.css + qml/html/cm/codemirror.js + qml/html/cm/comment.js + qml/html/cm/css.js + qml/html/cm/def.js + qml/html/cm/dialog.css + qml/html/cm/dialog.js + qml/html/cm/doc_comment.js + qml/html/cm/ecma5spec.js + qml/html/cm/errorannotation.js + qml/html/cm/fullscreen.css + qml/html/cm/fullscreen.js + qml/html/cm/htmlmixed.js + qml/html/cm/infer.js + qml/html/cm/javascript-hint.js + qml/html/cm/javascript.js + qml/html/cm/mark-selection.js + qml/html/cm/matchbrackets.js + qml/html/cm/search.js + qml/html/cm/searchcursor.js + qml/html/cm/show-hint.css + qml/html/cm/show-hint.js + qml/html/cm/signal.js + qml/html/cm/solarized.css + qml/html/cm/solidity.js + qml/html/cm/solidityToken.js + qml/html/cm/tern.js + qml/html/cm/ternserver.js + qml/html/cm/walk.js + qml/html/cm/xml.js + qml/html/codeeditor.html + qml/html/codeeditor.js + From dda4ccff3ef98ebcc556ff707b5f29cdf295f12b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 31 Mar 2015 13:49:39 +0200 Subject: [PATCH 09/11] Control stack size limit --- evmjit/libevmjit/BasicBlock.cpp | 1 + evmjit/libevmjit/BasicBlock.h | 4 +++ evmjit/libevmjit/Compiler.cpp | 3 ++ evmjit/libevmjit/RuntimeManager.cpp | 44 ++++++++++++++++++++++++++++- evmjit/libevmjit/RuntimeManager.h | 5 ++++ 5 files changed, 56 insertions(+), 1 deletion(-) diff --git a/evmjit/libevmjit/BasicBlock.cpp b/evmjit/libevmjit/BasicBlock.cpp index 93d73b991..a41743d0b 100644 --- a/evmjit/libevmjit/BasicBlock.cpp +++ b/evmjit/libevmjit/BasicBlock.cpp @@ -49,6 +49,7 @@ void BasicBlock::LocalStack::push(llvm::Value* _value) assert(_value->getType() == Type::Word); m_bblock.m_currentStack.push_back(_value); m_bblock.m_tosOffset += 1; + m_maxSize = std::max(m_maxSize, m_bblock.m_currentStack.size()); } llvm::Value* BasicBlock::LocalStack::pop() diff --git a/evmjit/libevmjit/BasicBlock.h b/evmjit/libevmjit/BasicBlock.h index 7469b7b69..5e19235a7 100644 --- a/evmjit/libevmjit/BasicBlock.h +++ b/evmjit/libevmjit/BasicBlock.h @@ -33,6 +33,9 @@ public: /// @param _index Index of value to be swaped. Must be > 0. void swap(size_t _index); + size_t getMaxSize() const { return m_maxSize; } + int getDiff() const { return m_bblock.m_tosOffset; } + private: LocalStack(BasicBlock& _owner); LocalStack(LocalStack const&) = delete; @@ -49,6 +52,7 @@ public: private: BasicBlock& m_bblock; + size_t m_maxSize = 0; ///< Max size reached by the stack. }; explicit BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest); diff --git a/evmjit/libevmjit/Compiler.cpp b/evmjit/libevmjit/Compiler.cpp index be3670874..1e1f8fe93 100644 --- a/evmjit/libevmjit/Compiler.cpp +++ b/evmjit/libevmjit/Compiler.cpp @@ -838,6 +838,9 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti // Block may have no terminator if the next instruction is a jump destination. if (!_basicBlock.llvm()->getTerminator()) m_builder.CreateBr(_nextBasicBlock); + + m_builder.SetInsertPoint(_basicBlock.llvm()->getFirstNonPHI()); + _runtimeManager.checkStackLimit(_basicBlock.localStack().getMaxSize(), _basicBlock.localStack().getDiff()); } diff --git a/evmjit/libevmjit/RuntimeManager.cpp b/evmjit/libevmjit/RuntimeManager.cpp index d1ccaea8a..f4db4d052 100644 --- a/evmjit/libevmjit/RuntimeManager.cpp +++ b/evmjit/libevmjit/RuntimeManager.cpp @@ -78,7 +78,7 @@ llvm::Twine getName(RuntimeData::Index _index) case RuntimeData::CodeSize: return "code"; case RuntimeData::CallDataSize: return "callDataSize"; case RuntimeData::Gas: return "gas"; - case RuntimeData::Number: return "number"; + case RuntimeData::Number: return "number"; case RuntimeData::Timestamp: return "timestamp"; } } @@ -101,6 +101,48 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB assert(m_memPtr->getType() == Array::getType()->getPointerTo()); m_envPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(rtPtr, 1), "env"); assert(m_envPtr->getType() == Type::EnvPtr); + + m_stackSize = m_builder.CreateAlloca(Type::Size, nullptr, "stackSize"); + m_builder.CreateStore(m_builder.getInt64(0), m_stackSize); + + llvm::Type* checkStackLimitArgs[] = {Type::Size->getPointerTo(), Type::Size, Type::Size, Type::BytePtr}; + m_checkStackLimit = llvm::Function::Create(llvm::FunctionType::get(Type::Void, checkStackLimitArgs, false), llvm::Function::PrivateLinkage, "stack.checkSize", getModule()); + m_checkStackLimit->setDoesNotThrow(); + m_checkStackLimit->setDoesNotCapture(1); + + auto checkBB = llvm::BasicBlock::Create(_builder.getContext(), "Check", m_checkStackLimit); + auto updateBB = llvm::BasicBlock::Create(_builder.getContext(), "Update", m_checkStackLimit); + auto outOfStackBB = llvm::BasicBlock::Create(_builder.getContext(), "OutOfStack", m_checkStackLimit); + + auto currSizePtr = &m_checkStackLimit->getArgumentList().front(); + currSizePtr->setName("currSize"); + auto max = currSizePtr->getNextNode(); + max->setName("max"); + auto diff = max->getNextNode(); + diff->setName("diff"); + auto jmpBuf = diff->getNextNode(); + jmpBuf->setName("jmpBuf"); + + InsertPointGuard guard{m_builder}; + m_builder.SetInsertPoint(checkBB); + auto currSize = m_builder.CreateLoad(currSizePtr, "cur"); + auto maxSize = m_builder.CreateNUWAdd(currSize, max, "maxSize"); + auto ok = m_builder.CreateICmpULT(maxSize, m_builder.getInt64(1024), "ok"); + m_builder.CreateCondBr(ok, updateBB, outOfStackBB, Type::expectTrue); + + m_builder.SetInsertPoint(updateBB); + auto newSize = m_builder.CreateNSWAdd(currSize, diff); + m_builder.CreateStore(newSize, currSizePtr); + m_builder.CreateRetVoid(); + + m_builder.SetInsertPoint(outOfStackBB); + abort(jmpBuf); + m_builder.CreateUnreachable(); +} + +void RuntimeManager::checkStackLimit(size_t _max, int _diff) +{ + createCall(m_checkStackLimit, {m_stackSize, m_builder.getInt64(_max), m_builder.getInt64(_diff), getJmpBuf()}); } llvm::Value* RuntimeManager::getRuntimePtr() diff --git a/evmjit/libevmjit/RuntimeManager.h b/evmjit/libevmjit/RuntimeManager.h index eb8dadf6f..c430a1098 100644 --- a/evmjit/libevmjit/RuntimeManager.h +++ b/evmjit/libevmjit/RuntimeManager.h @@ -48,6 +48,8 @@ public: static llvm::StructType* getRuntimeType(); static llvm::StructType* getRuntimeDataType(); + void checkStackLimit(size_t _max, int _diff); + private: llvm::Value* getPtr(RuntimeData::Index _index); void set(RuntimeData::Index _index, llvm::Value* _value); @@ -59,6 +61,9 @@ private: llvm::Value* m_memPtr = nullptr; llvm::Value* m_envPtr = nullptr; + llvm::Value* m_stackSize = nullptr; + llvm::Function* m_checkStackLimit = nullptr; + code_iterator m_codeBegin = {}; code_iterator m_codeEnd = {}; From 6083f49ba059c6acf40888c8b4ad633cb8d4a627 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Tue, 31 Mar 2015 15:14:49 +0200 Subject: [PATCH 10/11] eth_protocolVersion --- libweb3jsonrpc/WebThreeStubServerBase.cpp | 5 +++++ libweb3jsonrpc/WebThreeStubServerBase.h | 1 + libweb3jsonrpc/abstractwebthreestubserver.h | 7 +++++++ libweb3jsonrpc/spec.json | 1 + test/webthreestubclient.h | 10 ++++++++++ 5 files changed, 24 insertions(+) diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index cab986364..ba43a7b7e 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -284,6 +284,11 @@ bool WebThreeStubServerBase::net_listening() return network()->isNetworkStarted(); } +string WebThreeStubServerBase::eth_protocolVersion() +{ + return toJS(eth::c_protocolVersion); +} + string WebThreeStubServerBase::eth_coinbase() { return toJS(client()->address()); diff --git a/libweb3jsonrpc/WebThreeStubServerBase.h b/libweb3jsonrpc/WebThreeStubServerBase.h index 40265ac10..b57a54c87 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.h +++ b/libweb3jsonrpc/WebThreeStubServerBase.h @@ -77,6 +77,7 @@ public: virtual std::string net_peerCount(); virtual bool net_listening(); + virtual std::string eth_protocolVersion(); virtual std::string eth_coinbase(); virtual bool eth_mining(); virtual std::string eth_gasPrice(); diff --git a/libweb3jsonrpc/abstractwebthreestubserver.h b/libweb3jsonrpc/abstractwebthreestubserver.h index 516817d5c..6cc5de3e6 100644 --- a/libweb3jsonrpc/abstractwebthreestubserver.h +++ b/libweb3jsonrpc/abstractwebthreestubserver.h @@ -17,6 +17,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServerbindAndAddMethod(jsonrpc::Procedure("net_version", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::net_versionI); this->bindAndAddMethod(jsonrpc::Procedure("net_peerCount", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::net_peerCountI); this->bindAndAddMethod(jsonrpc::Procedure("net_listening", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::net_listeningI); + this->bindAndAddMethod(jsonrpc::Procedure("eth_protocolVersion", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_protocolVersionI); this->bindAndAddMethod(jsonrpc::Procedure("eth_coinbase", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_coinbaseI); this->bindAndAddMethod(jsonrpc::Procedure("eth_mining", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::eth_miningI); this->bindAndAddMethod(jsonrpc::Procedure("eth_gasPrice", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_gasPriceI); @@ -92,6 +93,11 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServernet_listening(); } + inline virtual void eth_protocolVersionI(const Json::Value &request, Json::Value &response) + { + (void)request; + response = this->eth_protocolVersion(); + } inline virtual void eth_coinbaseI(const Json::Value &request, Json::Value &response) { (void)request; @@ -302,6 +308,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServerCallMethod("eth_protocolVersion",p); + if (result.isString()) + return result.asString(); + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } std::string eth_coinbase() throw (jsonrpc::JsonRpcException) { Json::Value p; From 4cbc52afcf02455ec36dc7f8a4451a03ebe06247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 3 Apr 2015 16:16:29 +0200 Subject: [PATCH 11/11] Fix stack limit check --- evmjit/libevmjit/RuntimeManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evmjit/libevmjit/RuntimeManager.cpp b/evmjit/libevmjit/RuntimeManager.cpp index f4db4d052..dc7bc24a3 100644 --- a/evmjit/libevmjit/RuntimeManager.cpp +++ b/evmjit/libevmjit/RuntimeManager.cpp @@ -127,7 +127,7 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB m_builder.SetInsertPoint(checkBB); auto currSize = m_builder.CreateLoad(currSizePtr, "cur"); auto maxSize = m_builder.CreateNUWAdd(currSize, max, "maxSize"); - auto ok = m_builder.CreateICmpULT(maxSize, m_builder.getInt64(1024), "ok"); + auto ok = m_builder.CreateICmpULE(maxSize, m_builder.getInt64(1024), "ok"); m_builder.CreateCondBr(ok, updateBB, outOfStackBB, Type::expectTrue); m_builder.SetInsertPoint(updateBB);