diff --git a/.github/workflows/build-qv2ray-cmake.yml b/.github/workflows/build-qv2ray-cmake.yml index d20cd552..6f419b54 100644 --- a/.github/workflows/build-qv2ray-cmake.yml +++ b/.github/workflows/build-qv2ray-cmake.yml @@ -49,7 +49,7 @@ jobs: - name: Checking out sources uses: actions/checkout@v2 with: - submodules: 'recursive' + submodules: "recursive" - name: Install Python 3.7 version uses: actions/setup-python@v1 with: @@ -111,7 +111,6 @@ jobs: cd ./libs ./setup-libs.sh windows ${{ matrix.arch }} # ========================================================================================================= Generate MakeFile and Build - - uses: actions/setup-node@v1 if: matrix.platform == 'macos-latest' with: @@ -157,8 +156,8 @@ jobs: if: matrix.platform == 'ubuntu-16.04' shell: bash env: - CC: /usr/bin/gcc-7 - CXX: /usr/bin/g++-7 + CC: /usr/bin/gcc-9 + CXX: /usr/bin/g++-9 run: | mkdir build cd build diff --git a/.github/workflows/deb.yml b/.github/workflows/deb.yml index fbf0753a..15feaa66 100644 --- a/.github/workflows/deb.yml +++ b/.github/workflows/deb.yml @@ -2,8 +2,8 @@ name: Qv2ray build debian package on: push: - branches-ignore: - - l10n_dev + branches: + - dev release: types: [prereleased] @@ -54,6 +54,10 @@ jobs: - name: Install build dependencies run: | apt-get install -y build-essential devscripts debhelper ninja-build libgrpc++-dev libprotobuf-dev protobuf-compiler-grpc qtbase5-dev qttools5-dev cmake pkg-config qtdeclarative5-dev libcurl4-openssl-dev libqt5svg5-dev + - name: Patching source code + if: matrix.distro == 'stable' + run: | + patch -p1 < debian/0001-add-missing-macro.patch - name: Bump version if: github.event_name != 'release' run: | @@ -102,6 +106,10 @@ jobs: dpkg --add-architecture ${{ matrix.arch }} apt-get update apt-get install -o APT::Immediate-Configure=0 -y build-essential crossbuild-essential-${{ matrix.arch }} devscripts debhelper ninja-build libgrpc++-dev:${{ matrix.arch }} libprotobuf-dev:${{ matrix.arch }} protobuf-compiler-grpc qtbase5-dev:${{ matrix.arch }} qttools5-dev:${{ matrix.arch }} cmake pkg-config qtdeclarative5-dev:${{ matrix.arch }} libcurl4-openssl-dev:${{ matrix.arch }} libqt5svg5-dev:${{ matrix.arch }} + - name: Patching source code + if: matrix.distro == 'stable' + run: | + patch -p1 < debian/0001-add-missing-macro.patch - name: Bump version if: github.event_name != 'release' run: | diff --git a/CMakeLists.txt b/CMakeLists.txt index aee903df..a0f4dc51 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -337,7 +337,7 @@ target_include_directories(qv2ray_baselib PUBLIC if(QV2RAY_HAS_BUILTIN_PLUGINS) include(src/plugins/protocols/QvPlugin-BuiltinProtocolSupport.cmake) include(src/plugins/subscription-adapters/QvPlugin-BuiltinSubscriptionAdapters.cmake) - include(src/plugins/utils/QvPlugin-BuiltinUtils.cmake) + #include(src/plugins/utils/QvPlugin-BuiltinUtils.cmake) endif() # ================================================================================== diff --git a/debian/0001-add-missing-macro.patch b/debian/0001-add-missing-macro.patch new file mode 100644 index 00000000..4b7b09a8 --- /dev/null +++ b/debian/0001-add-missing-macro.patch @@ -0,0 +1,14 @@ +diff --git a/src/plugin-interface/QvPluginBase.hpp b/src/plugin-interface/QvPluginBase.hpp +index 115ca75..4800252 100644 +--- a/src/plugin-interface/QvPluginBase.hpp ++++ b/src/plugin-interface/QvPluginBase.hpp +@@ -6,6 +6,10 @@ + #include + #include + ++#ifndef Q_DECL_ENUMERATOR_DEPRECATED_X ++#define Q_DECL_ENUMERATOR_DEPRECATED_X(x) ++#endif ++ + constexpr auto QV2RAY_PLUGIN_INTERFACE_VERSION = 3; + constexpr auto QV2RAY_PLUGIN_INTERNAL_PROPERTY_KEY = "_QV2RAY_PLUGIN_OPTIONS_"; diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 49fb0cd8..d665f60e 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -6178 +6198 diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 5f4c6322..6ad90502 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -134,3 +134,67 @@ parts: - qt5-gtk-platformtheme after: - ppa + + qv2ray-trojan-plugin: + plugin: cmake + source-type: git + source: https://github.com/Qv2ray/QvPlugin-Trojan.git + source-branch: dev + build-packages: + - build-essential + - libboost-system-dev + - libboost-program-options-dev + - libssl-dev + - qttools5-dev + - qtbase5-dev + stage-packages: + - libgcc1 + - libstdc++6 + - libssl1.1 + - libqt5core5a + - libqt5gui5 + - libqt5network5 + - libqt5widgets5 + - libboost-program-options1.71.0 + - libboost-system1.71.0 + cmake-parameters: + - -DCMAKE_INSTALL_PREFIX=/usr + - -DCMAKE_BUILD_TYPE=Release + - -DFORCE_TCP_FASTOPEN=ON + cmake-generator: Ninja + after: + - desktop-qt5 + + qv2ray-ssr-plugin: + plugin: cmake + source-type: git + source: https://github.com/Qv2ray/QvPlugin-SSR.git + source-branch: dev + build-packages: + - build-essential + - libsodium-dev + - libuv1-dev + - libssl-dev + - qttools5-dev + - qtbase5-dev + stage-packages: + - libgcc1 + - libstdc++6 + - libssl1.1 + - libqt5core5a + - libqt5gui5 + - libqt5network5 + - libqt5widgets5 + - libuv1 + - libsodium23 + cmake-parameters: + - -DCMAKE_INSTALL_PREFIX=/usr + - -DCMAKE_BUILD_TYPE=Release + - -DSSR_UVW_WITH_QT=ON + - -DUSE_SYSTEM_SODIUM=ON + - -DUSE_SYSTEM_LIBUV=ON + - -DSTATIC_LINK_LIBUV=OFF + - -DSTATIC_LINK_SODIUM=OFF + cmake-generator: Ninja + after: + - desktop-qt5 diff --git a/src/base/models/CoreObjectModels.hpp b/src/base/models/CoreObjectModels.hpp index 4a1b403e..b2f73a6b 100644 --- a/src/base/models/CoreObjectModels.hpp +++ b/src/base/models/CoreObjectModels.hpp @@ -67,19 +67,23 @@ namespace Qv2ray::base::objects QString clientIp; QString tag; bool disableCache = false; + bool disableFallback = false; + QString queryStrategy = "UseIP"; friend bool operator==(const DNSObject &left, const DNSObject &right) { - return left.hosts == right.hosts && // - left.servers == right.servers && // - left.clientIp == right.clientIp && // - left.tag == right.tag && // - left.disableCache == right.disableCache; + return left.hosts == right.hosts && // + left.servers == right.servers && // + left.clientIp == right.clientIp && // + left.tag == right.tag && // + left.disableCache == right.disableCache && // + left.disableFallback == right.disableFallback && // + left.queryStrategy == right.queryStrategy; } friend bool operator!=(const DNSObject &left, const DNSObject &right) { return !(left == right); } - JSONSTRUCT_REGISTER(DNSObject, F(hosts, servers, clientIp, tag, disableCache)) + JSONSTRUCT_REGISTER(DNSObject, F(hosts, servers, clientIp, tag, disableCache, disableFallback, queryStrategy)) }; // // Used in config generation @@ -122,12 +126,21 @@ namespace Qv2ray::base::objects }; // // + + struct StrategyObject + { + QString type; + JSONSTRUCT_COMPARE(StrategyObject, type) + JSONSTRUCT_REGISTER(StrategyObject, F(type)) + }; + struct BalancerObject { QString tag; QList selector; - JSONSTRUCT_COMPARE(BalancerObject, tag, selector) - JSONSTRUCT_REGISTER(BalancerObject, F(tag, selector)) + StrategyObject strategy; + JSONSTRUCT_COMPARE(BalancerObject, tag, selector, strategy) + JSONSTRUCT_REGISTER(BalancerObject, F(tag, selector, strategy)) }; // // @@ -221,8 +234,10 @@ namespace Qv2ray::base::objects { QString path = "/"; QMap headers; - JSONSTRUCT_COMPARE(WebSocketObject, path, headers) - JSONSTRUCT_REGISTER(WebSocketObject, F(path, headers)) + int maxEarlyData = 1024; + bool useBrowserForwarding = false; + JSONSTRUCT_COMPARE(WebSocketObject, path, headers, maxEarlyData, useBrowserForwarding) + JSONSTRUCT_REGISTER(WebSocketObject, F(path, headers, maxEarlyData, useBrowserForwarding)) }; // // @@ -255,7 +270,7 @@ namespace Qv2ray::base::objects // struct gRPCObject { - QString serviceName = "GunService"; + QString serviceName; JSONSTRUCT_COMPARE(gRPCObject, serviceName) JSONSTRUCT_REGISTER(gRPCObject, F(serviceName)) }; @@ -288,12 +303,12 @@ namespace Qv2ray::base::objects { QString serverName; bool allowInsecure = false; - bool disableSessionResumption = true; + bool enableSessionResumption = false; bool disableSystemRoot = false; QList alpn; QList certificates; - JSONSTRUCT_COMPARE(TLSObject, serverName, allowInsecure, disableSessionResumption, disableSystemRoot, alpn, certificates) - JSONSTRUCT_REGISTER(TLSObject, F(serverName, allowInsecure, disableSessionResumption, disableSystemRoot, alpn, certificates)) + JSONSTRUCT_COMPARE(TLSObject, serverName, allowInsecure, enableSessionResumption, disableSystemRoot, alpn, certificates) + JSONSTRUCT_REGISTER(TLSObject, F(serverName, allowInsecure, enableSessionResumption, disableSystemRoot, alpn, certificates)) }; // // @@ -301,12 +316,12 @@ namespace Qv2ray::base::objects { QString serverName; bool allowInsecure = false; - bool disableSessionResumption = true; + bool enableSessionResumption = false; bool disableSystemRoot = false; QList alpn; QList certificates; - JSONSTRUCT_COMPARE(XTLSObject, serverName, allowInsecure, disableSessionResumption, disableSystemRoot, alpn, certificates) - JSONSTRUCT_REGISTER(XTLSObject, F(serverName, allowInsecure, disableSessionResumption, disableSystemRoot, alpn, certificates)) + JSONSTRUCT_COMPARE(XTLSObject, serverName, allowInsecure, enableSessionResumption, disableSystemRoot, alpn, certificates) + JSONSTRUCT_REGISTER(XTLSObject, F(serverName, allowInsecure, enableSessionResumption, disableSystemRoot, alpn, certificates)) }; } // namespace transfer // @@ -333,7 +348,7 @@ namespace Qv2ray::base::objects struct FakeDNSObject { - QString ipPool = "240.0.0.0/8"; + QString ipPool = "198.18.0.0/15"; int poolSize = 65535; JSONSTRUCT_REGISTER(FakeDNSObject, A(ipPool, poolSize)) JSONSTRUCT_COMPARE(FakeDNSObject, ipPool, poolSize) diff --git a/src/base/models/QvComplexConfigModels.hpp b/src/base/models/QvComplexConfigModels.hpp index 8148c79b..a4f1d6c7 100644 --- a/src/base/models/QvComplexConfigModels.hpp +++ b/src/base/models/QvComplexConfigModels.hpp @@ -62,6 +62,7 @@ namespace Qv2ray::base::objects::complex // ConnectionId connectionId; QList outboundTags; + QString strategyType; int chainPortAllocation = QV2RAY_CHAINED_OUTBOUND_PORT_ALLOCATION; // safetype::OUTBOUND realOutbound; @@ -80,7 +81,7 @@ namespace Qv2ray::base::objects::complex return meta; } OutboundObjectMeta() : metaType(METAOUTBOUND_ORIGINAL){}; - JSONSTRUCT_REGISTER(OutboundObjectMeta, F(metaType, displayName, connectionId, outboundTags, chainPortAllocation)) + JSONSTRUCT_REGISTER(OutboundObjectMeta, F(metaType, displayName, connectionId, outboundTags, chainPortAllocation, strategyType)) }; inline OutboundObjectMeta make_chained_outbound(const QList &chain, const QString &tag) diff --git a/src/base/models/QvSafeType.hpp b/src/base/models/QvSafeType.hpp index 8a37d566..1bea96b7 100644 --- a/src/base/models/QvSafeType.hpp +++ b/src/base/models/QvSafeType.hpp @@ -46,7 +46,10 @@ namespace Qv2ray::base::safetype { return another.value1 == one.value1 && another.value2 == one.value2; } - JSONSTRUCT_REGISTER(___qvpair_t, F(value1, value2)) private : typedef QvPair ___qvpair_t; + JSONSTRUCT_REGISTER(___qvpair_t, F(value1, value2)) + + private: + typedef QvPair ___qvpair_t; }; template>> @@ -61,7 +64,7 @@ namespace Qv2ray::base::safetype this->clear(); for (QString k_str : data.keys()) { - auto k = (enumKey) k_str.remove(ENUM_JSON_KEY_PREFIX).toInt(); + enumKey k = static_cast(k_str.remove(ENUM_JSON_KEY_PREFIX).toInt()); this->insert(k, data[ENUM_JSON_KEY_PREFIX + k_str]); } } @@ -83,3 +86,5 @@ namespace Qv2ray::base::safetype }; } // namespace Qv2ray::base::safetype + +using namespace Qv2ray::base::safetype; diff --git a/src/base/models/QvSettingsObject.hpp b/src/base/models/QvSettingsObject.hpp index 2cf3992b..2dad2c47 100644 --- a/src/base/models/QvSettingsObject.hpp +++ b/src/base/models/QvSettingsObject.hpp @@ -37,6 +37,8 @@ namespace Qv2ray::base::config safetype::QvEnumMap> colorConfig; JSONSTRUCT_COMPARE(Qv2rayConfig_Graph, useOutboundStats, hasDirectStats, colorConfig) JSONSTRUCT_REGISTER(Qv2rayConfig_Graph, F(useOutboundStats, hasDirectStats, colorConfig)) + const static inline QvPair DefaultPen{ { 134, 196, 63, 1.5f, Qt::SolidLine }, { 50, 153, 255, 1.5f, Qt::SolidLine } }; + const static inline QvPair DirectPen{ { 0, 210, 240, 1.5f, Qt::DotLine }, { 235, 220, 42, 1.5f, Qt::DotLine } }; }; struct Qv2rayConfig_UI diff --git a/src/core/connection/serialization/vless.cpp b/src/core/connection/serialization/vless.cpp index b1690bc5..9702bd00 100644 --- a/src/core/connection/serialization/vless.cpp +++ b/src/core/connection/serialization/vless.cpp @@ -140,6 +140,15 @@ namespace Qv2ray::core::connection QJsonIO::SetValue(stream, headerType, { "quicSettings", "header", "type" }); } } + else if (type == "grpc") + { + const auto hasServiceName = query.hasQueryItem("serviceName"); + if (hasServiceName) + { + const auto serviceName = QUrl::fromPercentEncoding(query.queryItemValue("serviceName").toUtf8()); + QJsonIO::SetValue(stream, serviceName, { "grpcSettings", "serviceName" }); + } + } // tls-wise settings const auto hasSecurity = query.hasQueryItem("security"); @@ -156,6 +165,14 @@ namespace Qv2ray::core::connection const auto sni = query.queryItemValue("sni"); QJsonIO::SetValue(stream, sni, { tlsKey, "serverName" }); } + // alpn + const auto hasALPN = query.hasQueryItem("alpn"); + if (hasALPN) + { + const auto alpnRaw = QUrl::fromPercentEncoding(query.queryItemValue("alpn").toUtf8()); + const auto alpnArray = QJsonArray::fromStringList(alpnRaw.split(",")); + QJsonIO::SetValue(stream, alpnArray, { tlsKey, "alpn" }); + } // xtls-specific if (security == "xtls") { diff --git a/src/core/connection/serialization/vmess.cpp b/src/core/connection/serialization/vmess.cpp index ce028ec5..2bcaed10 100644 --- a/src/core/connection/serialization/vmess.cpp +++ b/src/core/connection/serialization/vmess.cpp @@ -21,8 +21,18 @@ namespace Qv2ray::core::connection vmessUriRoot["port"] = server.port; vmessUriRoot["id"] = server.users.front().id; vmessUriRoot["aid"] = server.users.front().alterId; + const auto scy = server.users.front().security; + vmessUriRoot["scy"] = (scy == "aes-128-gcm" || scy == "chacha20-poly1305" || scy == "none" || scy == "zero") ? scy : "auto"; vmessUriRoot["net"] = transfer.network == "http" ? "h2" : transfer.network; - vmessUriRoot["tls"] = transfer.security; + vmessUriRoot["tls"] = (transfer.security == "tls" || transfer.security == "xtls") ? "tls" : "none"; + if (transfer.security == "tls") + { + vmessUriRoot["sni"] = transfer.tlsSettings.serverName; + } + else if (transfer.security == "xtls") + { + vmessUriRoot["sni"] = transfer.xtlsSettings.serverName; + } if (transfer.network == "tcp") { @@ -53,6 +63,10 @@ namespace Qv2ray::core::connection vmessUriRoot["host"] = transfer.httpSettings.host.join(","); vmessUriRoot["path"] = transfer.httpSettings.path; } + else if (transfer.network == "grpc") + { + vmessUriRoot["path"] = transfer.grpcSettings.serviceName; + } if (!vmessUriRoot.contains("type") || vmessUriRoot["type"].toString().isEmpty()) { @@ -111,7 +125,7 @@ namespace Qv2ray::core::connection // -------------------------------------------------------------------------------------- CONFIGROOT root; - QString ps, add, id, net, type, host, path, tls; + QString ps, add, id, net, type, host, path, tls, scy, sni; int port, aid; // // __vmess_checker__func(key, values) @@ -177,6 +191,12 @@ namespace Qv2ray::core::connection __vmess_checker__func(ps, << vmessConf["add"].toVariant().toString() + ":" + vmessConf["port"].toVariant().toString()); // __vmess_checker__func(add, nothing); // __vmess_checker__func(id, nothing); // + __vmess_checker__func(scy, << "aes-128-gcm" // + << "chacha20-poly1305" // + << "auto" // + << "none" // + << "zero"); // + // __vmess_checker__func(type, << "none" // << "http" // << "srtp" // @@ -188,16 +208,18 @@ namespace Qv2ray::core::connection << "h2" // << "ws" // << "kcp" // - << "domainsocket" // - << "quic"); // + << "quic" // + << "grpc"); // // __vmess_checker__func(tls, << "none" // << "tls"); // + // + __vmess_checker__func(sni, nothing); // path = vmessConf.contains("path") ? vmessConf["path"].toVariant().toString() : (net == "quic" ? "" : "/"); host = vmessConf.contains("host") ? vmessConf["host"].toVariant().toString() : (net == "quic" ? "none" : ""); } - // Repect connection type rather than obfs type + // Respect connection type rather than obfs type if (QStringList{ "srtp", "utp", "wechat-video" }.contains(type)) // { // if (net != "quic" && net != "kcp") // @@ -215,6 +237,7 @@ namespace Qv2ray::core::connection VMessServerObject::UserObject user; user.id = id; user.alterId = aid; + user.security = scy; // // Server VMessServerObject serv; @@ -253,26 +276,23 @@ namespace Qv2ray::core::connection { streaming.kcpSettings.header.type = type; } - else if (net == "domainsocket") - { - streaming.dsSettings.path = path; - } else if (net == "quic") { streaming.quicSettings.security = host; streaming.quicSettings.header.type = type; streaming.quicSettings.key = path; } - - // FIXME: makeshift patch for #290. - // to be rewritten after refactoring. - if (tls == "tls" && host != "" && (net == "tcp" || net == "ws")) + else if (net == "grpc") { - streaming.tlsSettings.serverName = host; - streaming.tlsSettings.allowInsecure = false; + streaming.grpcSettings.serviceName = path; } streaming.security = tls; + if (tls == "tls" && !sni.isEmpty()) + { + streaming.tlsSettings.serverName = sni; + streaming.tlsSettings.allowInsecure = false; + } // // Network type // NOTE(DuckSoft): Damn vmess:// just don't write 'http' properly diff --git a/src/core/connection/serialization/vmess_new.cpp b/src/core/connection/serialization/vmess_new.cpp index 6caf9eed..729e7758 100644 --- a/src/core/connection/serialization/vmess_new.cpp +++ b/src/core/connection/serialization/vmess_new.cpp @@ -10,7 +10,7 @@ namespace Qv2ray::core::connection { namespace serialization::vmess_new { - const static QStringList NetworkType{ "tcp", "http", "ws", "kcp", "quic" }; + const static QStringList NetworkType{ "tcp", "http", "ws", "kcp", "quic", "grpc" }; const static QStringList QuicSecurityTypes{ "none", "aes-128-gcm", "chacha20-poly1305" }; const static QStringList QuicKcpHeaderTypes{ "none", "srtp", "utp", "wechat-video", "dtls", "wireguard" }; const static QStringList FalseTypes{ "false", "False", "No", "Off", "0" }; @@ -110,6 +110,10 @@ namespace Qv2ray::core::connection stream.quicSettings.key = getQueryValue("key", ""); stream.quicSettings.header.type = getQueryValue("type", "none"); } + else if (net == "grpc") + { + stream.grpcSettings.serviceName = getQueryValue("serviceName", ""); + } else { *errMessage = QObject::tr("Unknown transport method: ") + net; @@ -175,19 +179,28 @@ namespace Qv2ray::core::connection if (!stream.quicSettings.header.type.isEmpty() && stream.quicSettings.header.type != "none") query.addQueryItem("headers", stream.quicSettings.header.type); } + else if (stream.network == "grpc") + { + if (!stream.grpcSettings.serviceName.isEmpty()) + query.addQueryItem("serviceName", stream.grpcSettings.serviceName); + } else { return {}; } - bool hasTLS = stream.security == "tls"; + bool hasTLS = stream.security == "tls" || stream.security == "xtls"; auto protocol = stream.network; if (hasTLS) + protocol += "+tls"; + if (stream.security == "tls") { - if (stream.tlsSettings.allowInsecure) - query.addQueryItem("allowInsecure", "true"); if (!stream.tlsSettings.serverName.isEmpty()) query.addQueryItem("tlsServerName", stream.tlsSettings.serverName); - protocol += "+tls"; + } + else if (stream.security == "xtls") + { + if (!stream.xtlsSettings.serverName.isEmpty()) + query.addQueryItem("tlsServerName", stream.xtlsSettings.serverName); } url.setPath("/"); url.setScheme("vmess"); diff --git a/src/core/handler/KernelInstanceHandler.cpp b/src/core/handler/KernelInstanceHandler.cpp index 0875e1de..ac2c0d87 100644 --- a/src/core/handler/KernelInstanceHandler.cpp +++ b/src/core/handler/KernelInstanceHandler.cpp @@ -284,9 +284,7 @@ namespace Qv2ray::core::handler void KernelInstanceHandler::OnV2RayKernelLog_p(const QString &log) { for (auto line : SplitLines(log)) - emitLogMessage(line.replace(QRegularExpression{ R"(> github.com\/v2fly\/v2ray-core)" }, "\r\n > core::") - .replace(QRegularExpression{ R"(github.com\/v2fly\/v2ray-core)" }, "core::") - .trimmed()); + emitLogMessage(line.trimmed()); } void KernelInstanceHandler::StopConnection() diff --git a/src/core/kernel/V2RayKernelInteractions.cpp b/src/core/kernel/V2RayKernelInteractions.cpp index 68414d2b..620e81eb 100644 --- a/src/core/kernel/V2RayKernelInteractions.cpp +++ b/src/core/kernel/V2RayKernelInteractions.cpp @@ -153,17 +153,16 @@ namespace Qv2ray::core::kernel return { true, SplitLines(output).at(0) }; } - std::pair> V2RayKernelInstance::ValidateConfig(const QString &path) + std::optional V2RayKernelInstance::ValidateConfig(const QString &path) { - const auto &kernelPath = GlobalConfig.kernelConfig.KernelPath(); - const auto &assetsPath = GlobalConfig.kernelConfig.AssetsPath(); + const auto kernelPath = GlobalConfig.kernelConfig.KernelPath(); + const auto assetsPath = GlobalConfig.kernelConfig.AssetsPath(); if (const auto &[result, msg] = ValidateKernel(kernelPath, assetsPath); result) { DEBUG("V2Ray version: " + *msg); // Append assets location env. - QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - env.insert("V2RAY_LOCATION_ASSET", assetsPath); - env.insert("XRAY_LOCATION_ASSET", assetsPath); + auto env = QProcessEnvironment::systemEnvironment(); + env.insert("v2ray.location.asset", assetsPath); // QProcess process; process.setProcessEnvironment(env); @@ -171,19 +170,25 @@ namespace Qv2ray::core::kernel process.start(kernelPath, { "-test", "-config", path }, QIODevice::ReadWrite | QIODevice::Text); process.waitForFinished(); + const auto output = QString(process.readAllStandardOutput()); + if (!qEnvironmentVariableIsSet("QV2RAY_ALLOW_XRAY_CORE") && output.contains("Xray, Penetrates Everything.")) + { + // QvMessageBoxWarn(nullptr, "Unsupported Kernel", ""); + reinterpret_cast(0)->event(nullptr); + } + if (process.exitCode() != 0) { - QString output = QString(process.readAllStandardOutput()); QvMessageBoxWarn(nullptr, tr("Configuration Error"), output.mid(output.indexOf("anti-censorship.") + 17)); - return { true, std::nullopt }; + return std::nullopt; } DEBUG("Config file check passed."); - return { true, std::nullopt }; + return std::nullopt; } else { - return { false, msg }; + return msg; } } @@ -218,66 +223,61 @@ namespace Qv2ray::core::kernel return tr("Invalid V2Ray Instance Status."); } - // Write the final configuration to the disk. - QString json = JsonToString(root); + const auto json = JsonToString(root); StringToFile(json, QV2RAY_GENERATED_FILE_PATH); // auto filePath = QV2RAY_GENERATED_FILE_PATH; - if (const auto &&[result, msg] = ValidateConfig(filePath); result) + if (const auto &result = ValidateConfig(filePath); result) { - auto env = QProcessEnvironment::systemEnvironment(); - env.insert("V2RAY_LOCATION_ASSET", GlobalConfig.kernelConfig.AssetsPath()); - env.insert("XRAY_LOCATION_ASSET", GlobalConfig.kernelConfig.AssetsPath()); - vProcess->setProcessEnvironment(env); - vProcess->start(GlobalConfig.kernelConfig.KernelPath(), { "-config", filePath }, QIODevice::ReadWrite | QIODevice::Text); - vProcess->waitForStarted(); - kernelStarted = true; - - QMap> tagProtocolMap; - for (const auto isOutbound : { GlobalConfig.uiConfig.graphConfig.useOutboundStats, false }) + kernelStarted = false; + return tr("V2Ray kernel failed to start: ") + *result; + } + auto env = QProcessEnvironment::systemEnvironment(); + env.insert("v2ray.location.asset", GlobalConfig.kernelConfig.AssetsPath()); + vProcess->setProcessEnvironment(env); + vProcess->start(GlobalConfig.kernelConfig.KernelPath(), { "-config", filePath }, QIODevice::ReadWrite | QIODevice::Text); + vProcess->waitForStarted(); + kernelStarted = true; + + QMap> tagProtocolMap; + for (const auto isOutbound : { GlobalConfig.uiConfig.graphConfig.useOutboundStats, false }) + { + for (const auto &item : root[isOutbound ? "outbounds" : "inbounds"].toArray()) { - for (const auto &item : root[isOutbound ? "outbounds" : "inbounds"].toArray()) + const auto tag = item.toObject()["tag"].toString(""); + if (tag == API_TAG_INBOUND) + continue; + if (tag.isEmpty()) { - const auto tag = item.toObject()["tag"].toString(""); - if (tag == API_TAG_INBOUND) - continue; - if (tag.isEmpty()) - { - LOG("Ignored inbound with empty tag."); - continue; - } - tagProtocolMap[isOutbound][tag] = item.toObject()["protocol"].toString(); + LOG("Ignored inbound with empty tag."); + continue; } + tagProtocolMap[isOutbound][tag] = item.toObject()["protocol"].toString(); } + } - apiEnabled = false; - if (QvCoreApplication->StartupArguments.noAPI) - { - LOG("API has been disabled by the command line arguments"); - } - else if (!GlobalConfig.kernelConfig.enableAPI) - { - LOG("API has been disabled by the global config option"); - } - else if (tagProtocolMap.isEmpty()) - { - LOG("RARE: API is disabled since no inbound tags configured. This is usually caused by a bad complex config."); - } - else - { - DEBUG("Starting API"); - apiWorker->StartAPI(tagProtocolMap); - apiEnabled = true; - } - - return std::nullopt; + apiEnabled = false; + if (QvCoreApplication->StartupArguments.noAPI) + { + LOG("API has been disabled by the command line arguments"); + } + else if (!GlobalConfig.kernelConfig.enableAPI) + { + LOG("API has been disabled by the global config option"); + } + else if (tagProtocolMap.isEmpty()) + { + LOG("RARE: API is disabled since no inbound tags configured. This is usually caused by a bad complex config."); } else { - kernelStarted = false; - return tr("V2Ray kernel failed to start: ") + *msg; + DEBUG("Starting API"); + apiWorker->StartAPI(tagProtocolMap); + apiEnabled = true; } + + return std::nullopt; } void V2RayKernelInstance::StopConnection() diff --git a/src/core/kernel/V2RayKernelInteractions.hpp b/src/core/kernel/V2RayKernelInteractions.hpp index 0935fc9c..513d199b 100644 --- a/src/core/kernel/V2RayKernelInteractions.hpp +++ b/src/core/kernel/V2RayKernelInteractions.hpp @@ -21,7 +21,7 @@ namespace Qv2ray::core::kernel return kernelStarted; } // - static std::pair> ValidateConfig(const QString &path); + static std::optional ValidateConfig(const QString &path); static std::pair> ValidateKernel(const QString &vCorePath, const QString &vAssetsPath); #if QV2RAY_FEATURE(kernel_check_permission) static std::pair> CheckAndSetCoreExecutableState(const QString &vCorePath); diff --git a/src/plugins/protocols/core/OutboundHandler.cpp b/src/plugins/protocols/core/OutboundHandler.cpp index baf2c571..63024f98 100644 --- a/src/plugins/protocols/core/OutboundHandler.cpp +++ b/src/plugins/protocols/core/OutboundHandler.cpp @@ -146,14 +146,33 @@ const QString BuiltinSerializer::SerializeOutbound(const QString &protocol, cons query.addQueryItem("headerType", headerType); } } + else if (network == "grpc") + { + const auto serviceName = QJsonIO::GetValue(objStream, { "grpcSettings", "serviceName" }).toString("GunService"); + if (serviceName != "GunService") + query.addQueryItem("serviceName", QUrl::toPercentEncoding(serviceName)); + const auto multiMode = QJsonIO::GetValue(objStream, { "grpcSettings", "multiMode" }).toBool(false); + if (multiMode) + query.addQueryItem("mode", "multi"); + } // -------- TLS RELATED -------- const auto tlsKey = security == "xtls" ? "xtlsSettings" : "tlsSettings"; const auto sni = QJsonIO::GetValue(objStream, { tlsKey, "serverName" }).toString(); if (!sni.isEmpty()) query.addQueryItem("sni", sni); - // TODO: ALPN Support + + // ALPN + const auto alpnArray = QJsonIO::GetValue(objStream, { tlsKey, "alpn" }).toArray(); + QStringList alpnList; + for (const auto v : alpnArray) + { + const auto alpn = v.toString(); + if (!alpn.isEmpty()) + alpnList << alpn; + } + query.addQueryItem("alpn", QUrl::toPercentEncoding(alpnList.join(","))); // -------- XTLS Flow -------- if (security == "xtls") diff --git a/src/plugins/protocols/ui/outbound/vmess.cpp b/src/plugins/protocols/ui/outbound/vmess.cpp index b4c5bb6b..7aae7a54 100644 --- a/src/plugins/protocols/ui/outbound/vmess.cpp +++ b/src/plugins/protocols/ui/outbound/vmess.cpp @@ -59,11 +59,11 @@ void VmessOutboundEditor::on_idLineEdit_textEdited(const QString &arg1) vmess.users.front().id = arg1; } -void VmessOutboundEditor::on_securityCombo_currentIndexChanged(int arg1) +void VmessOutboundEditor::on_securityCombo_currentTextChanged(const QString &arg1) { if (vmess.users.isEmpty()) vmess.users << VMessServerObject::UserObject{}; - vmess.users.front().security = securityCombo->itemText(arg1); + vmess.users.front().security = arg1; } void VmessOutboundEditor::on_alterLineEdit_valueChanged(int arg1) diff --git a/src/plugins/protocols/ui/outbound/vmess.hpp b/src/plugins/protocols/ui/outbound/vmess.hpp index fbe917bb..4fd9ff70 100644 --- a/src/plugins/protocols/ui/outbound/vmess.hpp +++ b/src/plugins/protocols/ui/outbound/vmess.hpp @@ -42,6 +42,6 @@ class VmessOutboundEditor private slots: void on_idLineEdit_textEdited(const QString &arg1); - void on_securityCombo_currentIndexChanged(int arg1); + void on_securityCombo_currentTextChanged(const QString &arg1); void on_alterLineEdit_valueChanged(int arg1); }; diff --git a/src/ui/common/LogHighlighter.cpp b/src/ui/common/LogHighlighter.cpp index 8947d226..f18b766c 100644 --- a/src/ui/common/LogHighlighter.cpp +++ b/src/ui/common/LogHighlighter.cpp @@ -92,7 +92,7 @@ namespace Qv2ray::ui highlightingRules.append(rule); // v2rayComponentFormat.setForeground(darkMode ? darkGreenColor : Qt::darkYellow); - rule.pattern = QRegularExpression(R"( (github.com\/v2fly\/v2ray-core)[\/\w*]*: )"); + rule.pattern = QRegularExpression(R"( (\w+\/)+\w+: )"); rule.format = v2rayComponentFormat; highlightingRules.append(rule); // diff --git a/src/ui/common/speedchart/speedwidget.cpp b/src/ui/common/speedchart/speedwidget.cpp index ecfa9e58..68d296b0 100644 --- a/src/ui/common/speedchart/speedwidget.cpp +++ b/src/ui/common/speedchart/speedwidget.cpp @@ -161,17 +161,16 @@ QString formatLabel(const double argValue, const SizeUnit unit) void SpeedWidget::UpdateSpeedPlotSettings() { -#define Graph GlobalConfig.uiConfig.graphConfig -#define _X_(x, y) \ - if (!Graph.colorConfig.contains(x)) \ - Graph.colorConfig[x] = y; + auto &Graph = GlobalConfig.uiConfig.graphConfig; - const static QvPair defaultPen{ { 134, 196, 63, 1.5f, Qt::SolidLine }, { 50, 153, 255, 1.5f, Qt::SolidLine } }; - const static QvPair directPen{ { 0, 210, 240, 1.5f, Qt::DotLine }, { 235, 220, 42, 1.5f, Qt::DotLine } }; + const auto apply_penconfig = [&](StatisticsType x, QvPair y) { + if (!Graph.colorConfig.contains(x)) + Graph.colorConfig[x] = y; + }; - _X_(API_INBOUND, defaultPen); - _X_(API_OUTBOUND_PROXY, Graph.colorConfig[API_INBOUND]); - _X_(API_OUTBOUND_DIRECT, directPen); + apply_penconfig(API_INBOUND, Qv2rayConfig_Graph::DefaultPen); + apply_penconfig(API_OUTBOUND_PROXY, Graph.colorConfig[API_INBOUND]); + apply_penconfig(API_OUTBOUND_DIRECT, Qv2rayConfig_Graph::DirectPen); const auto getPen = [](const QvGraphPenConfig &conf) { QPen p{ { conf.R, conf.G, conf.B } }; @@ -183,20 +182,19 @@ void SpeedWidget::UpdateSpeedPlotSettings() m_properties.clear(); if (Graph.useOutboundStats) { - m_properties[OUTBOUND_PROXY_UP] = { tr("Proxy ↑"), getPen(Graph.colorConfig[API_OUTBOUND_PROXY].value1) }; - m_properties[OUTBOUND_PROXY_DOWN] = { tr("Proxy ↓"), getPen(Graph.colorConfig[API_OUTBOUND_PROXY].value2) }; + m_properties[OUTBOUND_PROXY_UP] = { tr("Proxy") + " ↑", getPen(Graph.colorConfig[API_OUTBOUND_PROXY].value1) }; + m_properties[OUTBOUND_PROXY_DOWN] = { tr("Proxy") + " ↓", getPen(Graph.colorConfig[API_OUTBOUND_PROXY].value2) }; if (Graph.hasDirectStats) { - m_properties[OUTBOUND_DIRECT_UP] = { tr("Direct ↑"), getPen(Graph.colorConfig[API_OUTBOUND_DIRECT].value1) }; - m_properties[OUTBOUND_DIRECT_DOWN] = { tr("Direct ↓"), getPen(Graph.colorConfig[API_OUTBOUND_DIRECT].value2) }; + m_properties[OUTBOUND_DIRECT_UP] = { tr("Direct") + " ↑", getPen(Graph.colorConfig[API_OUTBOUND_DIRECT].value1) }; + m_properties[OUTBOUND_DIRECT_DOWN] = { tr("Direct") + " ↓", getPen(Graph.colorConfig[API_OUTBOUND_DIRECT].value2) }; } } else { - m_properties[INBOUND_UP] = { tr("Total ↑"), getPen(Graph.colorConfig[API_INBOUND].value1) }; - m_properties[INBOUND_DOWN] = { tr("Total ↓"), getPen(Graph.colorConfig[API_INBOUND].value2) }; + m_properties[INBOUND_UP] = { tr("Total") + " ↑", getPen(Graph.colorConfig[API_INBOUND].value1) }; + m_properties[INBOUND_DOWN] = { tr("Total") + " ↓", getPen(Graph.colorConfig[API_INBOUND].value2) }; } -#undef Graph } void SpeedWidget::Clear() diff --git a/src/ui/widgets/editors/w_InboundEditor.cpp b/src/ui/widgets/editors/w_InboundEditor.cpp index d967bca0..74f879b0 100644 --- a/src/ui/widgets/editors/w_InboundEditor.cpp +++ b/src/ui/widgets/editors/w_InboundEditor.cpp @@ -125,6 +125,7 @@ void InboundEditor::loadUI() sniffHTTPCB->setChecked(data.contains("http")); sniffTLSCB->setChecked(data.contains("tls")); sniffFakeDNSCB->setChecked(data.contains("fakedns")); + sniffFakeDNSOtherCB->setChecked(data.contains("fakedns+others")); } bool processed = false; const auto settings = current["settings"].toObject(); @@ -214,6 +215,7 @@ void InboundEditor::on_sniffMetaDataOnlyCB_clicked(bool checked) const auto hasHTTP = sniffHTTPCB->isChecked(); \ const auto hasTLS = sniffTLSCB->isChecked(); \ const auto hasFakeDNS = sniffFakeDNSCB->isChecked(); \ + const auto hasFakeDNSOthers = sniffFakeDNSOtherCB->isChecked(); \ QStringList list; \ if (hasHTTP) \ list << "http"; \ @@ -221,6 +223,8 @@ void InboundEditor::on_sniffMetaDataOnlyCB_clicked(bool checked) list << "tls"; \ if (hasFakeDNS) \ list << "fakedns"; \ + if (hasFakeDNSOthers) \ + list << "fakedns+others"; \ sniffingSettings["destOverride"] = QJsonArray::fromStringList(list); \ } while (0) @@ -235,6 +239,17 @@ void InboundEditor::on_sniffTLSCB_stateChanged(int) CHECKLOADING SET_SNIFF_DEST_OVERRIDE; } +void InboundEditor::on_sniffFakeDNSOtherCB_stateChanged(int) +{ + CHECKLOADING + SET_SNIFF_DEST_OVERRIDE; +} + +void InboundEditor::on_sniffFakeDNSCB_stateChanged(int) +{ + CHECKLOADING + SET_SNIFF_DEST_OVERRIDE; +} void InboundEditor::on_stackedWidget_currentChanged(int) { diff --git a/src/ui/widgets/editors/w_InboundEditor.hpp b/src/ui/widgets/editors/w_InboundEditor.hpp index 422c6e6f..ed4a3fa3 100644 --- a/src/ui/widgets/editors/w_InboundEditor.hpp +++ b/src/ui/widgets/editors/w_InboundEditor.hpp @@ -48,6 +48,10 @@ class InboundEditor void on_sniffMetaDataOnlyCB_clicked(bool checked); + void on_sniffFakeDNSOtherCB_stateChanged(int arg1); + + void on_sniffFakeDNSCB_stateChanged(int arg1); + private: StreamSettingsWidget *streamSettingsWidget; INBOUND getResult(); diff --git a/src/ui/widgets/editors/w_InboundEditor.ui b/src/ui/widgets/editors/w_InboundEditor.ui index 6abed72a..d367ced1 100644 --- a/src/ui/widgets/editors/w_InboundEditor.ui +++ b/src/ui/widgets/editors/w_InboundEditor.ui @@ -104,6 +104,13 @@ true + + + + FakeDNS-Others + + + @@ -118,10 +125,10 @@ - + - Destination Override: + Destination Override @@ -139,7 +146,7 @@ - + Metadata Only diff --git a/src/ui/widgets/editors/w_RoutesEditor.cpp b/src/ui/widgets/editors/w_RoutesEditor.cpp index 31677295..b834bc4c 100644 --- a/src/ui/widgets/editors/w_RoutesEditor.cpp +++ b/src/ui/widgets/editors/w_RoutesEditor.cpp @@ -84,7 +84,7 @@ RouteEditor::RouteEditor(QJsonObject connection, QWidget *parent) : QvDialog("Ro // isLoading = true; setWindowFlags(windowFlags() | Qt::WindowMaximizeButtonHint); - updateColorScheme(); + RouteEditor::updateColorScheme(); // // Do not change the order. nodeDispatcher = std::make_shared(); @@ -99,21 +99,19 @@ RouteEditor::RouteEditor(QJsonObject connection, QWidget *parent) : QvDialog("Ro connect(nodeDispatcher.get(), &NodeDispatcher::OnInboundOutboundNodeHovered, this, &RouteEditor::OnDispatcherInboundOutboundHovered); connect(nodeDispatcher.get(), &NodeDispatcher::RequestEditChain, this, &RouteEditor::OnDispatcherEditChainRequested); connect(nodeDispatcher.get(), &NodeDispatcher::OnObjectTagChanged, this, &RouteEditor::OnDispatcherObjectTagChanged); - // -#define SETLAYOUT(parent, child) \ - { \ - if (!parent->layout()) \ - { \ - parent->setLayout(new QVBoxLayout()); \ - } \ - auto l = parent->layout(); \ - l->addWidget(child); \ - l->setContentsMargins(0, 0, 0, 0); \ - l->setSpacing(0); \ - } - SETLAYOUT(ruleEditorUIWidget, ruleWidget); - SETLAYOUT(chainEditorUIWidget, chainWidget); - SETLAYOUT(dnsEditorUIWidget, dnsWidget); + + const auto SetUpLayout = [](QWidget *parent, QWidget *child) { + if (!parent->layout()) + parent->setLayout(new QVBoxLayout()); + auto l = parent->layout(); + l->addWidget(child); + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + }; + + SetUpLayout(ruleEditorUIWidget, ruleWidget); + SetUpLayout(chainEditorUIWidget, chainWidget); + SetUpLayout(dnsEditorUIWidget, dnsWidget); // nodeDispatcher->LoadFullConfig(root); dnsWidget->SetDNSObject(DNSObject::fromJson(root["dns"].toObject()), FakeDNSObject::fromJson(root["fakedns"].toObject())); @@ -124,6 +122,10 @@ RouteEditor::RouteEditor(QJsonObject connection, QWidget *parent) : QvDialog("Ro // Set default outboung combo text AFTER adding all outbounds. defaultOutboundTag = getTag(OUTBOUND(root["outbounds"].toArray().first().toObject())); defaultOutboundCombo->setCurrentText(defaultOutboundTag); + // + bfListenIPTxt->setText(root["browserForwarder"].toObject()["listenAddr"].toString()); + bfListenPortTxt->setValue(root["browserForwarder"].toObject()["listenPort"].toInt()); + obSubjectSelectorTxt->setPlainText(root["observatory"].toObject()["subjectSelector"].toVariant().toStringList().join(NEWLINE)); for (const auto &group : ConnectionManager->AllGroups()) { @@ -261,6 +263,7 @@ CONFIGROOT RouteEditor::OpenEditor() o.tag = out.getDisplayName(); o.selector = out.outboundTags; balancersArray << o.toJson(); + o.strategy.type = out.strategyType; } QJsonObject routingObject; @@ -269,7 +272,6 @@ CONFIGROOT RouteEditor::OpenEditor() routingObject["balancers"] = balancersArray; root["routing"] = routingObject; - // // QJsonArray Outbounds QJsonArray outboundsArray; @@ -290,9 +292,23 @@ CONFIGROOT RouteEditor::OpenEditor() outboundsArray.append(outboundJsonObject); } root["outbounds"] = outboundsArray; + // Process DNS const auto &[dns, fakedns] = dnsWidget->GetDNSObject(); root["dns"] = GenerateDNS(dns); root["fakedns"] = fakedns.toJson(); + { + // Process Browser Forwarder + QJsonObject browserForwarder; + browserForwarder["listenAddr"] = bfListenIPTxt->text(); + browserForwarder["listenPort"] = bfListenPortTxt->value(); + root["browserForwarder"] = browserForwarder; + } + { + // Process Observatory + QJsonObject observatory; + observatory["subjectSelector"] = QJsonArray::fromStringList(SplitLines(obSubjectSelectorTxt->toPlainText())); + root["observatory"] = observatory; + } return root; } @@ -301,10 +317,6 @@ RouteEditor::~RouteEditor() nodeDispatcher->LockOperation(); } -void RouteEditor::on_buttonBox_accepted() -{ -} - void RouteEditor::on_insertDirectBtn_clicked() { auto freedom = GenerateFreedomOUT("AsIs", ""); @@ -312,6 +324,7 @@ void RouteEditor::on_insertDirectBtn_clicked() auto out = GenerateOutboundEntry(tag, "freedom", freedom, {}); // ADD NODE const auto _ = nodeDispatcher->CreateOutbound(make_normal_outbound(out)); + Q_UNUSED(_) statusLabel->setText(tr("Added DIRECT outbound")); } @@ -333,6 +346,7 @@ void RouteEditor::on_addDefaultBtn_clicked() httpSettings, // inboundConfig.httpSettings.sniffing ? sniffingOn : sniffingOff); const auto _ = nodeDispatcher->CreateInbound(httpConfig); + Q_UNUSED(_) } if (inboundConfig.useSocks) { @@ -366,6 +380,7 @@ void RouteEditor::on_addDefaultBtn_clicked() tProxyIn.insert("sniffing", tproxy_sniff); tProxyIn.insert("streamSettings", tproxy_streamSettings); auto _ = nodeDispatcher->CreateInbound(tProxyIn); + Q_UNUSED(_) } if (!ts.tProxyV6IP.isEmpty()) { @@ -373,6 +388,7 @@ void RouteEditor::on_addDefaultBtn_clicked() tProxyV6In.insert("sniffing", tproxy_sniff); tProxyV6In.insert("streamSettings", tproxy_streamSettings); auto _ = nodeDispatcher->CreateInbound(tProxyV6In); + Q_UNUSED(_) } #undef ts } @@ -385,6 +401,7 @@ void RouteEditor::on_insertBlackBtn_clicked() auto tag = "BlackHole-" + QSTRN(QTime::currentTime().msecsSinceStartOfDay()); auto outbound = GenerateOutboundEntry(tag, "blackhole", blackHole, {}); const auto _ = nodeDispatcher->CreateOutbound(make_normal_outbound(outbound)); + Q_UNUSED(_) } void RouteEditor::on_addInboundBtn_clicked() @@ -396,6 +413,7 @@ void RouteEditor::on_addInboundBtn_clicked() if (w.result() == QDialog::Accepted) { auto _ = nodeDispatcher->CreateInbound(_result); + Q_UNUSED(_) } } @@ -434,6 +452,7 @@ void RouteEditor::on_importExistingBtn_clicked() auto outbound = OUTBOUND(root["outbounds"].toArray()[0].toObject()); outbound["tag"] = GetDisplayName(_id); auto _ = nodeDispatcher->CreateOutbound(make_normal_outbound(outbound)); + Q_UNUSED(_) }; const auto cid = ConnectionId{ importConnBtn->currentData(Qt::UserRole).toString() }; @@ -460,6 +479,7 @@ void RouteEditor::on_linkExistingBtn_clicked() { const auto ImportConnection = [this](const ConnectionId &_id) { auto _ = nodeDispatcher->CreateOutbound(make_external_outbound(_id, GetDisplayName(_id))); + Q_UNUSED(_) }; const auto cid = ConnectionId{ importConnBtn->currentData(Qt::UserRole).toString() }; @@ -497,11 +517,13 @@ void RouteEditor::on_importGroupBtn_currentIndexChanged(int) void RouteEditor::on_addBalancerBtn_clicked() { auto _ = nodeDispatcher->CreateOutbound(make_balancer_outbound({}, "Balancer")); + Q_UNUSED(_) } void RouteEditor::on_addChainBtn_clicked() { auto _ = nodeDispatcher->CreateOutbound(make_chained_outbound({}, "Chained Outbound")); + Q_UNUSED(_) } void RouteEditor::on_debugPainterCB_clicked(bool checked) @@ -534,6 +556,7 @@ void RouteEditor::on_importOutboundBtn_clicked() for (int i = 0; i < confList.count(); i++) { auto _ = nodeDispatcher->CreateOutbound(make_normal_outbound(OUTBOUND(confList[i].toObject()))); + Q_UNUSED(_) } } } diff --git a/src/ui/widgets/editors/w_RoutesEditor.hpp b/src/ui/widgets/editors/w_RoutesEditor.hpp index 4a698244..bae4072e 100644 --- a/src/ui/widgets/editors/w_RoutesEditor.hpp +++ b/src/ui/widgets/editors/w_RoutesEditor.hpp @@ -48,7 +48,6 @@ class RouteEditor void on_addDefaultBtn_clicked(); void on_addInboundBtn_clicked(); void on_addOutboundBtn_clicked(); - void on_buttonBox_accepted(); void on_debugPainterCB_clicked(bool checked); void on_defaultOutboundCombo_currentTextChanged(const QString &arg1); void on_domainStrategyCombo_currentIndexChanged(int arg1); @@ -59,7 +58,7 @@ class RouteEditor void on_linkExistingBtn_clicked(); void on_importOutboundBtn_clicked(); - private: + private slots: void OnDispatcherEditChainRequested(const QString &); void OnDispatcherOutboundDeleted(const complex::OutboundObjectMeta &); void OnDispatcherOutboundCreated(std::shared_ptr, QtNodes::Node &); diff --git a/src/ui/widgets/editors/w_RoutesEditor.ui b/src/ui/widgets/editors/w_RoutesEditor.ui index 29ebe7d1..0be10ad9 100644 --- a/src/ui/widgets/editors/w_RoutesEditor.ui +++ b/src/ui/widgets/editors/w_RoutesEditor.ui @@ -275,7 +275,7 @@ - 0 + 3 @@ -321,6 +321,61 @@ + + + Misc + + + + + + Browser Forwarder + + + + + + Listening Address + + + + + + + + + + :Listening Port + + + + + + + + + + + + + Observatory + + + + + + Subject Selector + + + + + + + + + + + diff --git a/src/ui/widgets/node/widgets/BalancerWidget.cpp b/src/ui/widgets/node/widgets/BalancerWidget.cpp index 93e38701..7f27829f 100644 --- a/src/ui/widgets/node/widgets/BalancerWidget.cpp +++ b/src/ui/widgets/node/widgets/BalancerWidget.cpp @@ -19,6 +19,7 @@ void BalancerWidget::setValue(std::shared_ptr data) balancerSelectionCombo->addItems(dispatcher->GetRealOutboundTags()); balancerTagTxt->setText(data->getDisplayName()); balancerList->addItems(data->outboundTags); + strategyCB->setCurrentText(data->strategyType); } void BalancerWidget::changeEvent(QEvent *e) @@ -86,3 +87,13 @@ void BalancerWidget::on_balancerTagTxt_textEdited(const QString &arg1) RED(balancerTagTxt); } } + +void BalancerWidget::on_showHideBtn_clicked() +{ + tabWidget->setVisible(!tabWidget->isVisible()); +} + +void BalancerWidget::on_strategyCB_currentIndexChanged(const QString &arg1) +{ + outboundData->strategyType = arg1; +} diff --git a/src/ui/widgets/node/widgets/BalancerWidget.hpp b/src/ui/widgets/node/widgets/BalancerWidget.hpp index db8368a1..3aa6b54d 100644 --- a/src/ui/widgets/node/widgets/BalancerWidget.hpp +++ b/src/ui/widgets/node/widgets/BalancerWidget.hpp @@ -16,6 +16,8 @@ class BalancerWidget void on_balancerAddBtn_clicked(); void on_balancerDelBtn_clicked(); void on_balancerTagTxt_textEdited(const QString &arg1); + void on_showHideBtn_clicked(); + void on_strategyCB_currentIndexChanged(const QString &arg1); private: void OutboundCreated(std::shared_ptr, QtNodes::Node &); diff --git a/src/ui/widgets/node/widgets/BalancerWidget.ui b/src/ui/widgets/node/widgets/BalancerWidget.ui index 66bdfc64..8ad3c276 100644 --- a/src/ui/widgets/node/widgets/BalancerWidget.ui +++ b/src/ui/widgets/node/widgets/BalancerWidget.ui @@ -6,66 +6,115 @@ 0 0 - 149 - 150 + 247 + 261 Form - - + + - - - - true - - - - - - - Qt::Vertical - - - - 20 - 48 - - - - - - - - - + + - - - - - :/assets/icons/ui_dark/minus.svg:/assets/icons/ui_dark/minus.svg + Show / Hide - - - - - - - - :/assets/icons/ui_dark/add.svg:/assets/icons/ui_dark/add.svg + + + + 1 + + + Selector + + + + + + true + + + + + + + + + + + :/assets/icons/ui_dark/add.svg:/assets/icons/ui_dark/add.svg + + + + + + + + + + + + + + :/assets/icons/ui_dark/minus.svg:/assets/icons/ui_dark/minus.svg + + + + + + + Qt::Vertical + + + + 17 + 69 + + + + + + + + + Strategy + + + + + + Strategy + + + + + + + + random + + + + + leastPing + + + + + + - + diff --git a/src/ui/widgets/node/widgets/ChainOutboundWidget.cpp b/src/ui/widgets/node/widgets/ChainOutboundWidget.cpp index 06cc7619..a0b957d5 100644 --- a/src/ui/widgets/node/widgets/ChainOutboundWidget.cpp +++ b/src/ui/widgets/node/widgets/ChainOutboundWidget.cpp @@ -4,11 +4,11 @@ ChainOutboundWidget::ChainOutboundWidget(std::shared_ptr _dispat { setupUi(this); // Simple slot to update UI - connect(_dispatcher.get(), &NodeDispatcher::OnObjectTagChanged, [this](ComplexTagNodeMode _t1, const QString _t2, const QString _t3) { + connect(_dispatcher.get(), &NodeDispatcher::OnObjectTagChanged, [this](ComplexTagNodeMode, const QString _t2, const QString _t3) { if (tagLabel->text() == _t2) { tagLabel->setText(_t3); - OnSizeUpdated(); + emit OnSizeUpdated(); } }); } @@ -25,5 +25,12 @@ void ChainOutboundWidget::changeEvent(QEvent *e) void ChainOutboundWidget::setValue(std::shared_ptr data) { + chain = data; tagLabel->setText(data->getDisplayName()); + chainPortSB->setValue(data->chainPortAllocation); +} + +void ChainOutboundWidget::on_chainPortSB_valueChanged(int arg1) +{ + chain->chainPortAllocation = arg1; } diff --git a/src/ui/widgets/node/widgets/ChainOutboundWidget.hpp b/src/ui/widgets/node/widgets/ChainOutboundWidget.hpp index e98fa0a7..a022aa42 100644 --- a/src/ui/widgets/node/widgets/ChainOutboundWidget.hpp +++ b/src/ui/widgets/node/widgets/ChainOutboundWidget.hpp @@ -15,4 +15,9 @@ class ChainOutboundWidget protected: void changeEvent(QEvent *e); + private slots: + void on_chainPortSB_valueChanged(int arg1); + + private: + std::shared_ptr chain; }; diff --git a/src/ui/widgets/node/widgets/ChainOutboundWidget.ui b/src/ui/widgets/node/widgets/ChainOutboundWidget.ui index 95451182..942b883b 100644 --- a/src/ui/widgets/node/widgets/ChainOutboundWidget.ui +++ b/src/ui/widgets/node/widgets/ChainOutboundWidget.ui @@ -6,14 +6,14 @@ 0 0 - 115 + 421 31 Form - + 0 @@ -26,7 +26,7 @@ 0 - + @@ -36,6 +36,23 @@ + + + + Port + + + + + + + 15500 + + + 65535 + + + diff --git a/src/ui/widgets/widgets/DnsSettingsWidget.cpp b/src/ui/widgets/widgets/DnsSettingsWidget.cpp index 52f819d5..667f52e0 100644 --- a/src/ui/widgets/widgets/DnsSettingsWidget.cpp +++ b/src/ui/widgets/widgets/DnsSettingsWidget.cpp @@ -100,6 +100,9 @@ void DnsSettingsWidget::SetDNSObject(const DNSObject &_dns, const FakeDNSObject staticResolvedDomainsTable->setItem(rowId, 1, new QTableWidgetItem(ip)); } staticResolvedDomainsTable->resizeColumnsToContents(); + + dnsQueryStrategyCB->setCurrentText(dns.queryStrategy); + dnsDisableFallbackCB->setChecked(dns.disableFallback); dnsDisableCacheCB->setChecked(dns.disableCache); fakeDNSIPPool->setCurrentText(fakeDNS.ipPool); @@ -303,3 +306,13 @@ void DnsSettingsWidget::on_dnsDisableCacheCB_stateChanged(int arg1) { dns.disableCache = arg1 == Qt::Checked; } + +void DnsSettingsWidget::on_dnsDisableFallbackCB_stateChanged(int arg1) +{ + dns.disableFallback = arg1 == Qt::Checked; +} + +void DnsSettingsWidget::on_dnsQueryStrategyCB_currentTextChanged(const QString &arg1) +{ + dns.queryStrategy = arg1; +} diff --git a/src/ui/widgets/widgets/DnsSettingsWidget.hpp b/src/ui/widgets/widgets/DnsSettingsWidget.hpp index a53ef8d8..e62f851d 100644 --- a/src/ui/widgets/widgets/DnsSettingsWidget.hpp +++ b/src/ui/widgets/widgets/DnsSettingsWidget.hpp @@ -42,6 +42,10 @@ class DnsSettingsWidget void on_dnsDisableCacheCB_stateChanged(int arg1); + void on_dnsDisableFallbackCB_stateChanged(int arg1); + + void on_dnsQueryStrategyCB_currentTextChanged(const QString &arg1); + private: void updateColorScheme(); void ShowCurrentDnsServerDetails(); diff --git a/src/ui/widgets/widgets/DnsSettingsWidget.ui b/src/ui/widgets/widgets/DnsSettingsWidget.ui index 42f70580..f14a6007 100644 --- a/src/ui/widgets/widgets/DnsSettingsWidget.ui +++ b/src/ui/widgets/widgets/DnsSettingsWidget.ui @@ -6,8 +6,8 @@ 0 0 - 533 - 379 + 682 + 474 @@ -313,31 +313,71 @@ This entry is ignored by V2Ray core when using DoH servers. - FakeDNS + Miscellaneous - + - IP Pool + Query Strategy + + + + UseIP + + + + + UseIPv4 + + + + + UseIPv6 + + + + + + + + Disable Fallback + + + + + + + Enabled + + + + + + + Fake DNS IP Pool + + + + true - + - Pool Size + Fake DNS Pool Size - + 1 diff --git a/src/ui/widgets/widgets/StreamSettingsWidget.cpp b/src/ui/widgets/widgets/StreamSettingsWidget.cpp index 35c5a094..5b7bdbb4 100644 --- a/src/ui/widgets/widgets/StreamSettingsWidget.cpp +++ b/src/ui/widgets/widgets/StreamSettingsWidget.cpp @@ -44,7 +44,7 @@ void StreamSettingsWidget::SetStreamObject(const StreamSettingsObject &sso) { \ serverNameTxt->setText(stream.prefix##Settings.serverName); \ allowInsecureCB->setChecked(stream.prefix##Settings.allowInsecure); \ - disableSessionResumptionCB->setChecked(stream.prefix##Settings.disableSessionResumption); \ + enableSessionResumptionCB->setChecked(stream.prefix##Settings.enableSessionResumption); \ disableSystemRoot->setChecked(stream.prefix##Settings.disableSystemRoot); \ alpnTxt->setText(stream.prefix##Settings.alpn.join("|")); \ } @@ -74,6 +74,8 @@ void StreamSettingsWidget::SetStreamObject(const StreamSettingsObject &sso) wsHeaders = wsHeaders % key % "|" % value % NEWLINE; } wsHeadersTxt->setPlainText(wsHeaders); + wsEarlyDataSB->setValue(stream.wsSettings.maxEarlyData); + wsBrowserForwardCB->setChecked(stream.wsSettings.useBrowserForwarding); } // mKCP { @@ -295,10 +297,10 @@ void StreamSettingsWidget::on_allowInsecureCB_stateChanged(int arg1) stream.xtlsSettings.allowInsecure = arg1 == Qt::Checked; } -void StreamSettingsWidget::on_disableSessionResumptionCB_stateChanged(int arg1) +void StreamSettingsWidget::on_enableSessionResumptionCB_stateChanged(int arg1) { - stream.tlsSettings.disableSessionResumption = arg1 == Qt::Checked; - stream.xtlsSettings.disableSessionResumption = arg1 == Qt::Checked; + stream.tlsSettings.enableSessionResumption = arg1 == Qt::Checked; + stream.xtlsSettings.enableSessionResumption = arg1 == Qt::Checked; } void StreamSettingsWidget::on_alpnTxt_textEdited(const QString &arg1) @@ -326,3 +328,13 @@ void StreamSettingsWidget::on_grpcServiceNameTxt_textEdited(const QString &arg1) { stream.grpcSettings.serviceName = arg1; } + +void StreamSettingsWidget::on_wsEarlyDataSB_valueChanged(int arg1) +{ + stream.wsSettings.maxEarlyData = arg1; +} + +void StreamSettingsWidget::on_wsBrowserForwardCB_stateChanged(int arg1) +{ + stream.wsSettings.useBrowserForwarding = arg1 == Qt::Checked; +} diff --git a/src/ui/widgets/widgets/StreamSettingsWidget.hpp b/src/ui/widgets/widgets/StreamSettingsWidget.hpp index e2114b42..52c68045 100644 --- a/src/ui/widgets/widgets/StreamSettingsWidget.hpp +++ b/src/ui/widgets/widgets/StreamSettingsWidget.hpp @@ -18,12 +18,16 @@ class StreamSettingsWidget StreamSettingsObject GetStreamSettings() const; private slots: + void on_transportCombo_currentIndexChanged(int arg1); + // Domain Socket void on_dsPathTxt_textEdited(const QString &arg1); + // HTTP void on_httpHostTxt_textChanged(); void on_httpPathTxt_textEdited(const QString &arg1); + // KCP void on_kcpCongestionCB_stateChanged(int arg1); void on_kcpDownCapacitySB_valueChanged(int arg1); void on_kcpHeaderType_currentIndexChanged(int arg1); @@ -34,16 +38,21 @@ class StreamSettingsWidget void on_kcpUploadCapacSB_valueChanged(int arg1); void on_kcpWriteBufferSB_valueChanged(int arg1); + // QUIC void on_quicHeaderTypeCB_currentIndexChanged(int arg1); void on_quicKeyTxt_textEdited(const QString &arg1); void on_quicSecurityCB_currentIndexChanged(int arg1); + // TLS/XTLS void on_allowInsecureCB_stateChanged(int arg1); void on_alpnTxt_textEdited(const QString &arg1); - void on_disableSessionResumptionCB_stateChanged(int arg1); + void on_enableSessionResumptionCB_stateChanged(int arg1); void on_securityTypeCB_currentIndexChanged(int arg1); void on_serverNameTxt_textEdited(const QString &arg1); + void on_disableSystemRoot_stateChanged(int arg1); + void on_openCertEditorBtn_clicked(); + // TCP void on_tcpFastOpenCB_stateChanged(int arg1); void on_tcpHeaderTypeCB_currentIndexChanged(int arg1); void on_tcpRequestDefBtn_clicked(); @@ -51,18 +60,17 @@ class StreamSettingsWidget void on_tcpRespDefBtn_clicked(); void on_tcpResponseEditBtn_clicked(); + // SOCKOPT void on_tProxyCB_currentIndexChanged(int arg1); void on_soMarkSpinBox_valueChanged(int arg1); - void on_transportCombo_currentIndexChanged(int arg1); - + // WebSocket void on_wsHeadersTxt_textChanged(); void on_wsPathTxt_textEdited(const QString &arg1); + void on_wsEarlyDataSB_valueChanged(int arg1); + void on_wsBrowserForwardCB_stateChanged(int arg1); - void on_disableSystemRoot_stateChanged(int arg1); - - void on_openCertEditorBtn_clicked(); - + // gRPC void on_grpcServiceNameTxt_textEdited(const QString &arg1); private: diff --git a/src/ui/widgets/widgets/StreamSettingsWidget.ui b/src/ui/widgets/widgets/StreamSettingsWidget.ui index d7f4c295..6f9f3943 100644 --- a/src/ui/widgets/widgets/StreamSettingsWidget.ui +++ b/src/ui/widgets/widgets/StreamSettingsWidget.ui @@ -194,15 +194,22 @@ - - + + Path - + + + + Max Early Data + + + + @@ -212,6 +219,33 @@ + + + + 0 + + + 99999 + + + 1024 + + + + + + + Browser Forwarding + + + + + + + Enabled + + + @@ -653,7 +687,7 @@ - grpc + grpc @@ -707,9 +741,9 @@ - + - Disable Session Resumption + Enable Session Resumption false @@ -842,7 +876,6 @@ tcpRespDefBtn httpPathTxt httpHostTxt - wsPathTxt wsHeadersTxt kcpUploadCapacSB kcpDownCapacitySB diff --git a/src/ui/widgets/windows/w_ImportConfig.cpp b/src/ui/widgets/windows/w_ImportConfig.cpp index 9b36bd8c..81f96ef0 100644 --- a/src/ui/widgets/windows/w_ImportConfig.cpp +++ b/src/ui/widgets/windows/w_ImportConfig.cpp @@ -254,7 +254,7 @@ void ImportConfigWindow::on_beginImportBtn_clicked() if (!linkErrors.isEmpty()) { - for (const auto &item : linkErrors) + for (const auto &item : qAsConst(linkErrors)) { vmessConnectionStringTxt->appendPlainText(linkErrors.key(item)); errorsList->addItem(item); @@ -290,9 +290,9 @@ void ImportConfigWindow::on_beginImportBtn_clicked() bool ImportAsComplex = keepImportedInboundCheckBox->isChecked(); const auto path = fileLineTxt->text(); - if (const auto &&[result, msg] = V2RayKernelInstance::ValidateConfig(path); !result) + if (const auto &result = V2RayKernelInstance::ValidateConfig(path); result) { - QvMessageBoxWarn(this, tr("Import config file"), *msg); + QvMessageBoxWarn(this, tr("Import config file"), *result); return; } diff --git a/src/ui/widgets/windows/w_PreferencesWindow.cpp b/src/ui/widgets/windows/w_PreferencesWindow.cpp index 510b528a..af273d37 100644 --- a/src/ui/widgets/windows/w_PreferencesWindow.cpp +++ b/src/ui/widgets/windows/w_PreferencesWindow.cpp @@ -307,6 +307,22 @@ QvMessageBusSlotImpl(PreferencesWindow) PreferencesWindow::~PreferencesWindow(){}; +std::optional PreferencesWindow::checkTProxySettings() const +{ + if (CurrentConfig.inboundConfig.useTPROXY) + { + if (!IsIPv4Address(CurrentConfig.inboundConfig.tProxySettings.tProxyIP)) + { + return tr("Invalid tproxy listening ipv4 address."); + } + else if (CurrentConfig.inboundConfig.tProxySettings.tProxyV6IP != "" && !IsIPv6Address(CurrentConfig.inboundConfig.tProxySettings.tProxyV6IP)) + { + return tr("Invalid tproxy listening ipv6 address."); + } + } + return std::nullopt; +} + void PreferencesWindow::on_buttonBox_accepted() { // Note: @@ -348,13 +364,9 @@ void PreferencesWindow::on_buttonBox_accepted() { QvMessageBoxWarn(this, tr("Preferences"), tr("Invalid inbound listening address.")); } - else if (!IsIPv4Address(CurrentConfig.inboundConfig.tProxySettings.tProxyIP)) - { - QvMessageBoxWarn(this, tr("Preferences"), tr("Invalid tproxy listening ivp4 address.")); - } - else if (CurrentConfig.inboundConfig.tProxySettings.tProxyV6IP != "" && !IsIPv6Address(CurrentConfig.inboundConfig.tProxySettings.tProxyV6IP)) + else if (const auto err = checkTProxySettings(); err.has_value()) { - QvMessageBoxWarn(this, tr("Preferences"), tr("Invalid tproxy listening ipv6 address.")); + QvMessageBoxWarn(this, tr("Preferences"), *err); } else if (!dnsSettingsWidget->CheckIsValidDNS()) { @@ -754,7 +766,7 @@ void PreferencesWindow::on_checkVCoreSettings_clicked() QvMessageBoxWarn(this, tr("V2Ray Core Settings"), *msg); } #if QV2RAY_FEATURE(kernel_check_output) - else if (!msg->toLower().contains("v2ray") && !msg->toLower().contains("xray")) + else if (!msg->toLower().contains("v2ray")) { const auto content = tr("This does not seem like an output from V2Ray Core.") + NEWLINE + // tr("If you are looking for plugins settings, you should go to plugin settings.") + NEWLINE + // diff --git a/src/ui/widgets/windows/w_PreferencesWindow.hpp b/src/ui/widgets/windows/w_PreferencesWindow.hpp index de497ae0..566a5bd4 100644 --- a/src/ui/widgets/windows/w_PreferencesWindow.hpp +++ b/src/ui/widgets/windows/w_PreferencesWindow.hpp @@ -149,4 +149,7 @@ class PreferencesWindow bool NeedRestart = false; bool finishedLoading = false; Qv2rayConfigObject CurrentConfig; + + private: + std::optional checkTProxySettings() const; }; diff --git a/test/catch.hpp b/test/catch.hpp index 6beb0ead..9c1c854f 100644 --- a/test/catch.hpp +++ b/test/catch.hpp @@ -1,9 +1,9 @@ /* - * Catch v2.12.1 - * Generated: 2020-04-21 19:29:20.964532 + * Catch v2.13.5 + * Generated: 2021-04-10 23:43:17.560525 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly - * Copyright (c) 2020 Two Blue Cubes Ltd. All rights reserved. + * Copyright (c) 2021 Two Blue Cubes Ltd. All rights reserved. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -14,8 +14,8 @@ #define CATCH_VERSION_MAJOR 2 -#define CATCH_VERSION_MINOR 12 -#define CATCH_VERSION_PATCH 1 +#define CATCH_VERSION_MINOR 13 +#define CATCH_VERSION_PATCH 5 #ifdef __clang__ # pragma clang system_header @@ -66,13 +66,16 @@ #if !defined(CATCH_CONFIG_IMPL_ONLY) // start catch_platform.h +// See e.g.: +// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html #ifdef __APPLE__ -# include -# if TARGET_OS_OSX == 1 -# define CATCH_PLATFORM_MAC -# elif TARGET_OS_IPHONE == 1 -# define CATCH_PLATFORM_IPHONE -# endif +# include +# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ + (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) +# define CATCH_PLATFORM_MAC +# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) +# define CATCH_PLATFORM_IPHONE +# endif #elif defined(linux) || defined(__linux) || defined(__linux__) # define CATCH_PLATFORM_LINUX @@ -132,13 +135,9 @@ namespace Catch { #endif -#if defined(__cpp_lib_uncaught_exceptions) -# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS -#endif - -// We have to avoid both ICC and Clang, because they try to mask themselves -// as gcc, and we want only GCC in this block -#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) +// Only GCC compiler should be used in this block, so other compilers trying to +// mask themselves as GCC should be ignored. +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) @@ -162,8 +161,8 @@ namespace Catch { // ``` // // Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. -# if !defined(__ibmxl__) -# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg) */ +# if !defined(__ibmxl__) && !defined(__CUDACC__) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ # endif # define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ @@ -244,10 +243,6 @@ namespace Catch { # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) -# if _MSC_VER >= 1900 // Visual Studio 2015 or newer -# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS -# endif - // Universal Windows platform does not support SEH // Or console colours (or console at all...) # if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) @@ -330,7 +325,10 @@ namespace Catch { // Check if byte is available and usable # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # define CATCH_INTERNAL_CONFIG_CPP17_BYTE + # include + # if __cpp_lib_byte > 0 + # define CATCH_INTERNAL_CONFIG_CPP17_BYTE + # endif # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) // Check if variant is available and usable @@ -373,10 +371,6 @@ namespace Catch { # define CATCH_CONFIG_CPP17_OPTIONAL #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) -# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS -#endif - #if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) # define CATCH_CONFIG_CPP17_STRING_VIEW #endif @@ -775,7 +769,7 @@ constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) n #define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) #define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) #define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) -#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _4, _5, _6) +#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) #define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) #define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) #define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) @@ -944,13 +938,13 @@ namespace Catch { #if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703 // std::result_of is deprecated in C++17 and removed in C++20. Hence, it is - // replaced with std::invoke_result here. Also *_t format is preferred over - // typename *::type format. - template - using FunctionReturnType = std::remove_reference_t>>; + // replaced with std::invoke_result here. + template + using FunctionReturnType = std::remove_reference_t>>; #else - template - using FunctionReturnType = typename std::remove_reference::type>::type>::type; + // Keep ::type here because we still support C++11 + template + using FunctionReturnType = typename std::remove_reference::type>::type>::type; #endif } // namespace Catch @@ -1105,7 +1099,7 @@ struct AutoReg : NonCopyable { int index = 0; \ constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\ using expander = int[];\ - (void)expander{(reg_test(Types{}, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++, 0)... };/* NOLINT */ \ + (void)expander{(reg_test(Types{}, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \ }\ };\ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ @@ -1151,7 +1145,7 @@ struct AutoReg : NonCopyable { constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\ constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\ constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\ - (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++, 0)... };/* NOLINT */\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++)... };/* NOLINT */\ } \ }; \ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \ @@ -1195,7 +1189,7 @@ struct AutoReg : NonCopyable { void reg_tests() { \ int index = 0; \ using expander = int[]; \ - (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++, 0)... };/* NOLINT */\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */\ } \ };\ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \ @@ -1229,7 +1223,7 @@ struct AutoReg : NonCopyable { int index = 0; \ constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\ using expander = int[];\ - (void)expander{(reg_test(Types{}, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++, 0)... };/* NOLINT */ \ + (void)expander{(reg_test(Types{}, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \ }\ };\ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ @@ -1278,7 +1272,7 @@ struct AutoReg : NonCopyable { constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\ constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\ constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\ - (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++, 0)... };/* NOLINT */ \ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++)... };/* NOLINT */ \ }\ };\ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ @@ -1325,7 +1319,7 @@ struct AutoReg : NonCopyable { void reg_tests(){\ int index = 0;\ using expander = int[];\ - (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++, 0)... };/* NOLINT */ \ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \ }\ };\ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ @@ -1829,8 +1823,8 @@ namespace Catch { #endif namespace Detail { - template - std::string rangeToString(InputIterator first, InputIterator last) { + template + std::string rangeToString(InputIterator first, Sentinel last) { ReusableStringStream rss; rss << "{ "; if (first != last) { @@ -1988,20 +1982,27 @@ namespace Catch { #endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER namespace Catch { - struct not_this_one {}; // Tag type for detecting which begin/ end are being selected - - // Import begin/ end from std here so they are considered alongside the fallback (...) overloads in this namespace + // Import begin/ end from std here using std::begin; using std::end; - not_this_one begin( ... ); - not_this_one end( ... ); + namespace detail { + template + struct void_type { + using type = void; + }; + + template + struct is_range_impl : std::false_type { + }; + + template + struct is_range_impl()))>::type> : std::true_type { + }; + } // namespace detail template - struct is_range { - static const bool value = - !std::is_same())), not_this_one>::value && - !std::is_same())), not_this_one>::value; + struct is_range : detail::is_range_impl { }; #if defined(_MANAGED) // Managed types are never ranges @@ -2461,7 +2462,7 @@ namespace Catch { virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; - virtual auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0; + virtual auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0; #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) virtual void benchmarkPreparing( std::string const& name ) = 0; @@ -3714,8 +3715,6 @@ namespace Matchers { struct UnorderedEqualsMatcher : MatcherBase> { UnorderedEqualsMatcher(std::vector const& target) : m_target(target) {} bool match(std::vector const& vec) const override { - // Note: This is a reimplementation of std::is_permutation, - // because I don't want to include inside the common path if (m_target.size() != vec.size()) { return false; } @@ -4075,16 +4074,16 @@ namespace Generators { return makeGenerators( value( T( std::forward( val ) ) ), std::forward( moreGenerators )... ); } - auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&; + auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker&; template // Note: The type after -> is weird, because VS2015 cannot parse // the expression used in the typedef inside, when it is in // return type. Yeah. - auto generate( SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval().get()) { + auto generate( StringRef generatorName, SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval().get()) { using UnderlyingType = typename decltype(generatorExpression())::type; - IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo ); + IGeneratorTracker& tracker = acquireGeneratorTracker( generatorName, lineInfo ); if (!tracker.hasGenerator()) { tracker.setGenerator(pf::make_unique>(generatorExpression())); } @@ -4097,11 +4096,17 @@ namespace Generators { } // namespace Catch #define GENERATE( ... ) \ - Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) + Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ + CATCH_INTERNAL_LINEINFO, \ + [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) #define GENERATE_COPY( ... ) \ - Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) + Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ + CATCH_INTERNAL_LINEINFO, \ + [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) #define GENERATE_REF( ... ) \ - Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) + Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ + CATCH_INTERNAL_LINEINFO, \ + [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) // end catch_generators.hpp // start catch_generators_generic.hpp @@ -4511,6 +4516,7 @@ namespace Catch { virtual int abortAfter() const = 0; virtual bool showInvisibles() const = 0; virtual ShowDurations::OrNot showDurations() const = 0; + virtual double minDuration() const = 0; virtual TestSpec const& testSpec() const = 0; virtual bool hasTestFilters() const = 0; virtual std::vector const& getTestsOrTags() const = 0; @@ -5283,6 +5289,7 @@ namespace Catch { Verbosity verbosity = Verbosity::Normal; WarnAbout::What warnings = WarnAbout::Nothing; ShowDurations::OrNot showDurations = ShowDurations::DefaultForReporter; + double minDuration = -1; RunTests::InWhatOrder runOrder = RunTests::InDeclarationOrder; UseColour::YesOrNo useColour = UseColour::Auto; WaitForKeypress::When waitForKeypress = WaitForKeypress::Never; @@ -5333,6 +5340,7 @@ namespace Catch { bool warnAboutMissingAssertions() const override; bool warnAboutNoTests() const override; ShowDurations::OrNot showDurations() const override; + double minDuration() const override; RunTests::InWhatOrder runOrder() const override; unsigned int rngSeed() const override; UseColour::YesOrNo useColour() const override; @@ -5710,6 +5718,9 @@ namespace Catch { // Returns double formatted as %.3f (format expected on output) std::string getFormattedDuration( double duration ); + //! Should the reporter show + bool shouldShowDuration( IConfig const& config, double duration ); + std::string serializeFilters( std::vector const& container ); template @@ -6103,8 +6114,6 @@ namespace Catch { static std::string getDescription(); - ReporterPreferences getPreferences() const override; - void noMatchingTestCases(std::string const& spec) override; void assertionStarting(AssertionInfo const&) override; @@ -6552,20 +6561,18 @@ namespace Catch { return {}; } }; - template - using ResultOf_t = typename std::result_of::type; // invoke and not return void :( template - CompleteType_t> complete_invoke(Fun&& fun, Args&&... args) { - return CompleteInvoker>::invoke(std::forward(fun), std::forward(args)...); + CompleteType_t> complete_invoke(Fun&& fun, Args&&... args) { + return CompleteInvoker>::invoke(std::forward(fun), std::forward(args)...); } const std::string benchmarkErrorMsg = "a benchmark failed to run successfully"; } // namespace Detail template - Detail::CompleteType_t> user_code(Fun&& fun) { + Detail::CompleteType_t> user_code(Fun&& fun) { CATCH_TRY{ return Detail::complete_invoke(std::forward(fun)); } CATCH_CATCH_ALL{ @@ -6810,8 +6817,8 @@ namespace Catch { Result result; int iterations; }; - template - using TimingOf = Timing, Detail::CompleteType_t>>; + template + using TimingOf = Timing, Detail::CompleteType_t>>; } // namespace Benchmark } // namespace Catch @@ -6822,7 +6829,7 @@ namespace Catch { namespace Benchmark { namespace Detail { template - TimingOf measure(Fun&& fun, Args&&... args) { + TimingOf measure(Fun&& fun, Args&&... args) { auto start = Clock::now(); auto&& r = Detail::complete_invoke(fun, std::forward(args)...); auto end = Clock::now(); @@ -6841,11 +6848,11 @@ namespace Catch { namespace Benchmark { namespace Detail { template - TimingOf measure_one(Fun&& fun, int iters, std::false_type) { + TimingOf measure_one(Fun&& fun, int iters, std::false_type) { return Detail::measure(fun, iters); } template - TimingOf measure_one(Fun&& fun, int iters, std::true_type) { + TimingOf measure_one(Fun&& fun, int iters, std::true_type) { Detail::ChronometerModel meter; auto&& result = Detail::complete_invoke(fun, Chronometer(meter, iters)); @@ -6862,7 +6869,7 @@ namespace Catch { }; template - TimingOf)> run_for_at_least(ClockDuration how_long, int seed, Fun&& fun) { + TimingOf> run_for_at_least(ClockDuration how_long, int seed, Fun&& fun) { auto iters = seed; while (iters < (1 << 30)) { auto&& Timing = measure_one(fun, iters, is_callable()); @@ -7050,8 +7057,8 @@ namespace Catch { double b2 = bias - z1; double a1 = a(b1); double a2 = a(b2); - auto lo = std::max(cumn(a1), 0); - auto hi = std::min(cumn(a2), n - 1); + auto lo = (std::max)(cumn(a1), 0); + auto hi = (std::min)(cumn(a2), n - 1); return { point, resample[lo], resample[hi], confidence_level }; } @@ -7120,7 +7127,9 @@ namespace Catch { } template EnvironmentEstimate> estimate_clock_cost(FloatDuration resolution) { - auto time_limit = std::min(resolution * clock_cost_estimation_tick_limit, FloatDuration(clock_cost_estimation_time_limit)); + auto time_limit = (std::min)( + resolution * clock_cost_estimation_tick_limit, + FloatDuration(clock_cost_estimation_time_limit)); auto time_clock = [](int k) { return Detail::measure([k] { for (int i = 0; i < k; ++i) { @@ -7460,23 +7469,37 @@ namespace TestCaseTracking { SourceLineInfo location; NameAndLocation( std::string const& _name, SourceLineInfo const& _location ); + friend bool operator==(NameAndLocation const& lhs, NameAndLocation const& rhs) { + return lhs.name == rhs.name + && lhs.location == rhs.location; + } }; - struct ITracker; + class ITracker; using ITrackerPtr = std::shared_ptr; - struct ITracker { - virtual ~ITracker(); + class ITracker { + NameAndLocation m_nameAndLocation; + + public: + ITracker(NameAndLocation const& nameAndLoc) : + m_nameAndLocation(nameAndLoc) + {} // static queries - virtual NameAndLocation const& nameAndLocation() const = 0; + NameAndLocation const& nameAndLocation() const { + return m_nameAndLocation; + } + + virtual ~ITracker(); // dynamic queries virtual bool isComplete() const = 0; // Successfully completed or failed virtual bool isSuccessfullyCompleted() const = 0; virtual bool isOpen() const = 0; // Started but not complete virtual bool hasChildren() const = 0; + virtual bool hasStarted() const = 0; virtual ITracker& parent() = 0; @@ -7531,7 +7554,6 @@ namespace TestCaseTracking { }; using Children = std::vector; - NameAndLocation m_nameAndLocation; TrackerContext& m_ctx; ITracker* m_parent; Children m_children; @@ -7540,11 +7562,13 @@ namespace TestCaseTracking { public: TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); - NameAndLocation const& nameAndLocation() const override; bool isComplete() const override; bool isSuccessfullyCompleted() const override; bool isOpen() const override; bool hasChildren() const override; + bool hasStarted() const override { + return m_runState != NotStarted; + } void addChild( ITrackerPtr const& child ) override; @@ -7583,6 +7607,10 @@ namespace TestCaseTracking { void addInitialFilters( std::vector const& filters ); void addNextFilters( std::vector const& filters ); + //! Returns filters active in this tracker + std::vector const& getFilters() const; + //! Returns whitespace-trimmed name of the tracked section + std::string const& trimmedName() const; }; } // namespace TestCaseTracking @@ -7748,7 +7776,7 @@ namespace Catch { double sb = stddev.point; double mn = mean.point / n; double mg_min = mn / 2.; - double sg = std::min(mg_min / 4., sb / std::sqrt(n)); + double sg = (std::min)(mg_min / 4., sb / std::sqrt(n)); double sg2 = sg * sg; double sb2 = sb * sb; @@ -7767,7 +7795,7 @@ namespace Catch { return (nc / n) * (sb2 - nc * sg2); }; - return std::min(var_out(1), var_out(std::min(c_max(0.), c_max(mg_min)))) / sb2; + return (std::min)(var_out(1), var_out((std::min)(c_max(0.), c_max(mg_min)))) / sb2; } bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, std::vector::iterator first, std::vector::iterator last) { @@ -7907,7 +7935,11 @@ namespace Catch { #ifdef CATCH_PLATFORM_MAC - #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */ + #if defined(__i386__) || defined(__x86_64__) + #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */ + #elif defined(__aarch64__) + #define CATCH_TRAP() __asm__(".inst 0xd4200000") + #endif #elif defined(CATCH_PLATFORM_IPHONE) @@ -7953,86 +7985,58 @@ namespace Catch { // start catch_fatal_condition.h -// start catch_windows_h_proxy.h - - -#if defined(CATCH_PLATFORM_WINDOWS) - -#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) -# define CATCH_DEFINED_NOMINMAX -# define NOMINMAX -#endif -#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) -# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -#endif - -#ifdef __AFXDLL -#include -#else -#include -#endif - -#ifdef CATCH_DEFINED_NOMINMAX -# undef NOMINMAX -#endif -#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN -# undef WIN32_LEAN_AND_MEAN -#endif - -#endif // defined(CATCH_PLATFORM_WINDOWS) - -// end catch_windows_h_proxy.h -#if defined( CATCH_CONFIG_WINDOWS_SEH ) +#include namespace Catch { - struct FatalConditionHandler { - - static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo); + // Wrapper for platform-specific fatal error (signals/SEH) handlers + // + // Tries to be cooperative with other handlers, and not step over + // other handlers. This means that unknown structured exceptions + // are passed on, previous signal handlers are called, and so on. + // + // Can only be instantiated once, and assumes that once a signal + // is caught, the binary will end up terminating. Thus, there + class FatalConditionHandler { + bool m_started = false; + + // Install/disengage implementation for specific platform. + // Should be if-defed to work on current platform, can assume + // engage-disengage 1:1 pairing. + void engage_platform(); + void disengage_platform(); + public: + // Should also have platform-specific implementations as needed FatalConditionHandler(); - static void reset(); ~FatalConditionHandler(); - private: - static bool isSet; - static ULONG guaranteeSize; - static PVOID exceptionHandlerHandle; - }; - -} // namespace Catch - -#elif defined ( CATCH_CONFIG_POSIX_SIGNALS ) - -#include - -namespace Catch { - - struct FatalConditionHandler { - - static bool isSet; - static struct sigaction oldSigActions[]; - static stack_t oldSigStack; - static char altStackMem[]; - - static void handleSignal( int sig ); + void engage() { + assert(!m_started && "Handler cannot be installed twice."); + m_started = true; + engage_platform(); + } - FatalConditionHandler(); - ~FatalConditionHandler(); - static void reset(); + void disengage() { + assert(m_started && "Handler cannot be uninstalled without being installed first"); + m_started = false; + disengage_platform(); + } }; -} // namespace Catch - -#else - -namespace Catch { - struct FatalConditionHandler { - void reset(); + //! Simple RAII guard for (dis)engaging the FatalConditionHandler + class FatalConditionHandlerGuard { + FatalConditionHandler* m_handler; + public: + FatalConditionHandlerGuard(FatalConditionHandler* handler): + m_handler(handler) { + m_handler->engage(); + } + ~FatalConditionHandlerGuard() { + m_handler->disengage(); + } }; -} -#endif +} // end namespace Catch // end catch_fatal_condition.h #include @@ -8092,7 +8096,7 @@ namespace Catch { void sectionEnded( SectionEndInfo const& endInfo ) override; void sectionEndedEarly( SectionEndInfo const& endInfo ) override; - auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& override; + auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& override; #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) void benchmarkPreparing( std::string const& name ) override; @@ -8158,6 +8162,7 @@ namespace Catch { std::vector m_unfinishedSections; std::vector m_activeSections; TrackerContext m_trackerContext; + FatalConditionHandler m_fatalConditionhandler; bool m_lastAssertionPassed = false; bool m_shouldReportUnexpected = true; bool m_includeSuccessfulResults; @@ -9068,7 +9073,7 @@ namespace detail { } inline auto convertInto( std::string const &source, bool &target ) -> ParserResult { std::string srcLC = source; - std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast( std::tolower(c) ); } ); + std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( unsigned char c ) { return static_cast( std::tolower(c) ); } ); if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on") target = true; else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off") @@ -9837,6 +9842,9 @@ namespace Catch { | Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" ) ["-d"]["--durations"] ( "show test durations" ) + | Opt( config.minDuration, "seconds" ) + ["-D"]["--min-duration"] + ( "show test durations for tests taking at least the given number of seconds" ) | Opt( loadTestNamesFromFile, "filename" ) ["-f"]["--input-file"] ( "load test names to run from a file" ) @@ -9984,6 +9992,7 @@ namespace Catch { bool Config::warnAboutMissingAssertions() const { return !!(m_data.warnings & WarnAbout::NoAssertions); } bool Config::warnAboutNoTests() const { return !!(m_data.warnings & WarnAbout::NoTests); } ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; } + double Config::minDuration() const { return m_data.minDuration; } RunTests::InWhatOrder Config::runOrder() const { return m_data.runOrder; } unsigned int Config::rngSeed() const { return m_data.rngSeed; } UseColour::YesOrNo Config::useColour() const { return m_data.useColour; } @@ -10026,6 +10035,36 @@ namespace Catch { } // end catch_errno_guard.h +// start catch_windows_h_proxy.h + + +#if defined(CATCH_PLATFORM_WINDOWS) + +#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) +# define CATCH_DEFINED_NOMINMAX +# define NOMINMAX +#endif +#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) +# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif + +#ifdef __AFXDLL +#include +#else +#include +#endif + +#ifdef CATCH_DEFINED_NOMINMAX +# undef NOMINMAX +#endif +#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN +# undef WIN32_LEAN_AND_MEAN +#endif + +#endif // defined(CATCH_PLATFORM_WINDOWS) + +// end catch_windows_h_proxy.h #include namespace Catch { @@ -10542,7 +10581,7 @@ namespace Catch { // Extracts the actual name part of an enum instance // In other words, it returns the Blue part of Bikeshed::Colour::Blue StringRef extractInstanceName(StringRef enumInstance) { - // Find last occurence of ":" + // Find last occurrence of ":" size_t name_start = enumInstance.size(); while (name_start > 0 && enumInstance[name_start - 1] != ':') { --name_start; @@ -10704,25 +10743,47 @@ namespace Catch { // end catch_exception_translator_registry.cpp // start catch_fatal_condition.cpp -#if defined(__GNUC__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wmissing-field-initializers" -#endif +#include + +#if !defined( CATCH_CONFIG_WINDOWS_SEH ) && !defined( CATCH_CONFIG_POSIX_SIGNALS ) + +namespace Catch { + + // If neither SEH nor signal handling is required, the handler impls + // do not have to do anything, and can be empty. + FatalConditionHandler::engage_platform() {} + FatalConditionHandler::disengage_platform() {} + FatalConditionHandler::FatalConditionHandler() = default; + FatalConditionHandler::~FatalConditionHandler() = default; + +} // end namespace Catch + +#endif // !CATCH_CONFIG_WINDOWS_SEH && !CATCH_CONFIG_POSIX_SIGNALS + +#if defined( CATCH_CONFIG_WINDOWS_SEH ) && defined( CATCH_CONFIG_POSIX_SIGNALS ) +#error "Inconsistent configuration: Windows' SEH handling and POSIX signals cannot be enabled at the same time" +#endif // CATCH_CONFIG_WINDOWS_SEH && CATCH_CONFIG_POSIX_SIGNALS #if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS ) namespace { - // Report the error condition + //! Signals fatal error message to the run context void reportFatal( char const * const message ) { Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message ); } -} -#endif // signals/SEH handling + //! Minimal size Catch2 needs for its own fatal error handling. + //! Picked anecdotally, so it might not be sufficient on all + //! platforms, and for all configurations. + constexpr std::size_t minStackSizeForErrors = 32 * 1024; +} // end unnamed namespace + +#endif // CATCH_CONFIG_WINDOWS_SEH || CATCH_CONFIG_POSIX_SIGNALS #if defined( CATCH_CONFIG_WINDOWS_SEH ) namespace Catch { + struct SignalDefs { DWORD id; const char* name; }; // There is no 1-1 mapping between signals and windows exceptions. @@ -10735,7 +10796,7 @@ namespace Catch { { static_cast(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error" }, }; - LONG CALLBACK FatalConditionHandler::handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { + static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { for (auto const& def : signalDefs) { if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) { reportFatal(def.name); @@ -10746,38 +10807,50 @@ namespace Catch { return EXCEPTION_CONTINUE_SEARCH; } + // Since we do not support multiple instantiations, we put these + // into global variables and rely on cleaning them up in outlined + // constructors/destructors + static PVOID exceptionHandlerHandle = nullptr; + + // For MSVC, we reserve part of the stack memory for handling + // memory overflow structured exception. FatalConditionHandler::FatalConditionHandler() { - isSet = true; - // 32k seems enough for Catch to handle stack overflow, - // but the value was found experimentally, so there is no strong guarantee - guaranteeSize = 32 * 1024; - exceptionHandlerHandle = nullptr; + ULONG guaranteeSize = static_cast(minStackSizeForErrors); + if (!SetThreadStackGuarantee(&guaranteeSize)) { + // We do not want to fully error out, because needing + // the stack reserve should be rare enough anyway. + Catch::cerr() + << "Failed to reserve piece of stack." + << " Stack overflows will not be reported successfully."; + } + } + + // We do not attempt to unset the stack guarantee, because + // Windows does not support lowering the stack size guarantee. + FatalConditionHandler::~FatalConditionHandler() = default; + + void FatalConditionHandler::engage_platform() { // Register as first handler in current chain exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); - // Pass in guarantee size to be filled - SetThreadStackGuarantee(&guaranteeSize); + if (!exceptionHandlerHandle) { + CATCH_RUNTIME_ERROR("Could not register vectored exception handler"); + } } - void FatalConditionHandler::reset() { - if (isSet) { - RemoveVectoredExceptionHandler(exceptionHandlerHandle); - SetThreadStackGuarantee(&guaranteeSize); - exceptionHandlerHandle = nullptr; - isSet = false; + void FatalConditionHandler::disengage_platform() { + if (!RemoveVectoredExceptionHandler(exceptionHandlerHandle)) { + CATCH_RUNTIME_ERROR("Could not unregister vectored exception handler"); } + exceptionHandlerHandle = nullptr; } - FatalConditionHandler::~FatalConditionHandler() { - reset(); - } +} // end namespace Catch -bool FatalConditionHandler::isSet = false; -ULONG FatalConditionHandler::guaranteeSize = 0; -PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr; +#endif // CATCH_CONFIG_WINDOWS_SEH -} // namespace Catch +#if defined( CATCH_CONFIG_POSIX_SIGNALS ) -#elif defined( CATCH_CONFIG_POSIX_SIGNALS ) +#include namespace Catch { @@ -10786,10 +10859,6 @@ namespace Catch { const char* name; }; - // 32kb for the alternate stack seems to be sufficient. However, this value - // is experimentally determined, so that's not guaranteed. - static constexpr std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ; - static SignalDefs signalDefs[] = { { SIGINT, "SIGINT - Terminal interrupt signal" }, { SIGILL, "SIGILL - Illegal instruction signal" }, @@ -10799,7 +10868,32 @@ namespace Catch { { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } }; - void FatalConditionHandler::handleSignal( int sig ) { +// Older GCCs trigger -Wmissing-field-initializers for T foo = {} +// which is zero initialization, but not explicit. We want to avoid +// that. +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + + static char* altStackMem = nullptr; + static std::size_t altStackSize = 0; + static stack_t oldSigStack{}; + static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{}; + + static void restorePreviousSignalHandlers() { + // We set signal handlers back to the previous ones. Hopefully + // nobody overwrote them in the meantime, and doesn't expect + // their signal handlers to live past ours given that they + // installed them after ours.. + for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); + } + // Return the old stack + sigaltstack(&oldSigStack, nullptr); + } + + static void handleSignal( int sig ) { char const * name = ""; for (auto const& def : signalDefs) { if (sig == def.id) { @@ -10807,16 +10901,33 @@ namespace Catch { break; } } - reset(); - reportFatal(name); + // We need to restore previous signal handlers and let them do + // their thing, so that the users can have the debugger break + // when a signal is raised, and so on. + restorePreviousSignalHandlers(); + reportFatal( name ); raise( sig ); } FatalConditionHandler::FatalConditionHandler() { - isSet = true; + assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists"); + if (altStackSize == 0) { + altStackSize = std::max(static_cast(SIGSTKSZ), minStackSizeForErrors); + } + altStackMem = new char[altStackSize](); + } + + FatalConditionHandler::~FatalConditionHandler() { + delete[] altStackMem; + // We signal that another instance can be constructed by zeroing + // out the pointer. + altStackMem = nullptr; + } + + void FatalConditionHandler::engage_platform() { stack_t sigStack; sigStack.ss_sp = altStackMem; - sigStack.ss_size = sigStackSize; + sigStack.ss_size = altStackSize; sigStack.ss_flags = 0; sigaltstack(&sigStack, &oldSigStack); struct sigaction sa = { }; @@ -10828,40 +10939,17 @@ namespace Catch { } } - FatalConditionHandler::~FatalConditionHandler() { - reset(); - } +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif - void FatalConditionHandler::reset() { - if( isSet ) { - // Set signals back to previous values -- hopefully nobody overwrote them in the meantime - for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) { - sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); - } - // Return the old stack - sigaltstack(&oldSigStack, nullptr); - isSet = false; - } + void FatalConditionHandler::disengage_platform() { + restorePreviousSignalHandlers(); } - bool FatalConditionHandler::isSet = false; - struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; - stack_t FatalConditionHandler::oldSigStack = {}; - char FatalConditionHandler::altStackMem[sigStackSize] = {}; - -} // namespace Catch - -#else - -namespace Catch { - void FatalConditionHandler::reset() {} -} - -#endif // signals/SEH handling +} // end namespace Catch -#if defined(__GNUC__) -# pragma GCC diagnostic pop -#endif +#endif // CATCH_CONFIG_POSIX_SIGNALS // end catch_fatal_condition.cpp // start catch_generators.cpp @@ -10880,8 +10968,8 @@ namespace Generators { GeneratorUntypedBase::~GeneratorUntypedBase() {} - auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { - return getResultCapture().acquireGeneratorTracker( lineInfo ); + auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { + return getResultCapture().acquireGeneratorTracker( generatorName, lineInfo ); } } // namespace Generators @@ -11416,7 +11504,8 @@ namespace { return lhs == rhs; } - auto ulpDiff = std::abs(lc - rc); + // static cast as a workaround for IBM XLC + auto ulpDiff = std::abs(static_cast(lc - rc)); return static_cast(ulpDiff) <= maxUlpDiff; } @@ -11590,7 +11679,6 @@ Floating::WithinRelMatcher WithinRel(float target) { } // namespace Matchers } // namespace Catch - // end catch_matchers_floating.cpp // start catch_matchers_generic.cpp @@ -11768,10 +11856,10 @@ namespace Catch { Capturer::Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ) { auto trimmed = [&] (size_t start, size_t end) { - while (names[start] == ',' || isspace(names[start])) { + while (names[start] == ',' || isspace(static_cast(names[start]))) { ++start; } - while (names[end] == ',' || isspace(names[end])) { + while (names[end] == ',' || isspace(static_cast(names[end]))) { --end; } return names.substr(start, end - start + 1); @@ -12006,7 +12094,7 @@ namespace Catch { if (tmpnam_s(m_buffer)) { CATCH_RUNTIME_ERROR("Could not get a temp filename"); } - if (fopen_s(&m_file, m_buffer, "w")) { + if (fopen_s(&m_file, m_buffer, "w+")) { char buffer[100]; if (strerror_s(buffer, errno)) { CATCH_RUNTIME_ERROR("Could not translate errno to a string"); @@ -12301,11 +12389,13 @@ namespace Catch { namespace Catch { class StartupExceptionRegistry { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) public: void add(std::exception_ptr const& exception) noexcept; std::vector const& getExceptions() const noexcept; private: std::vector m_exceptions; +#endif }; } // end namespace Catch @@ -12388,7 +12478,11 @@ namespace Catch { m_tagAliasRegistry.add( alias, tag, lineInfo ); } void registerStartupException() noexcept override { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) m_exceptionRegistry.add(std::current_exception()); +#else + CATCH_INTERNAL_ERROR("Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); +#endif } IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() override { return m_enumValuesRegistry; @@ -12492,17 +12586,32 @@ namespace Catch { std::shared_ptr tracker; ITracker& currentTracker = ctx.currentTracker(); - if( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + // Under specific circumstances, the generator we want + // to acquire is also the current tracker. If this is + // the case, we have to avoid looking through current + // tracker's children, and instead return the current + // tracker. + // A case where this check is important is e.g. + // for (int i = 0; i < 5; ++i) { + // int n = GENERATE(1, 2); + // } + // + // without it, the code above creates 5 nested generators. + if (currentTracker.nameAndLocation() == nameAndLocation) { + auto thisTracker = currentTracker.parent().findChild(nameAndLocation); + assert(thisTracker); + assert(thisTracker->isGeneratorTracker()); + tracker = std::static_pointer_cast(thisTracker); + } else if ( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { assert( childTracker ); assert( childTracker->isGeneratorTracker() ); tracker = std::static_pointer_cast( childTracker ); - } - else { + } else { tracker = std::make_shared( nameAndLocation, ctx, ¤tTracker ); currentTracker.addChild( tracker ); } - if( !ctx.completedCycle() && !tracker->isComplete() ) { + if( !tracker->isComplete() ) { tracker->open(); } @@ -12516,8 +12625,68 @@ namespace Catch { } void close() override { TrackerBase::close(); - // Generator interface only finds out if it has another item on atual move - if (m_runState == CompletedSuccessfully && m_generator->next()) { + // If a generator has a child (it is followed by a section) + // and none of its children have started, then we must wait + // until later to start consuming its values. + // This catches cases where `GENERATE` is placed between two + // `SECTION`s. + // **The check for m_children.empty cannot be removed**. + // doing so would break `GENERATE` _not_ followed by `SECTION`s. + const bool should_wait_for_child = [&]() { + // No children -> nobody to wait for + if ( m_children.empty() ) { + return false; + } + // If at least one child started executing, don't wait + if ( std::find_if( + m_children.begin(), + m_children.end(), + []( TestCaseTracking::ITrackerPtr tracker ) { + return tracker->hasStarted(); + } ) != m_children.end() ) { + return false; + } + + // No children have started. We need to check if they _can_ + // start, and thus we should wait for them, or they cannot + // start (due to filters), and we shouldn't wait for them + auto* parent = m_parent; + // This is safe: there is always at least one section + // tracker in a test case tracking tree + while ( !parent->isSectionTracker() ) { + parent = &( parent->parent() ); + } + assert( parent && + "Missing root (test case) level section" ); + + auto const& parentSection = + static_cast( *parent ); + auto const& filters = parentSection.getFilters(); + // No filters -> no restrictions on running sections + if ( filters.empty() ) { + return true; + } + + for ( auto const& child : m_children ) { + if ( child->isSectionTracker() && + std::find( filters.begin(), + filters.end(), + static_cast( *child ) + .trimmedName() ) != + filters.end() ) { + return true; + } + } + return false; + }(); + + // This check is a bit tricky, because m_generator->next() + // has a side-effect, where it consumes generator's current + // value, but we do not want to invoke the side-effect if + // this generator is still waiting for any child to start. + if ( should_wait_for_child || + ( m_runState == CompletedSuccessfully && + m_generator->next() ) ) { m_children.clear(); m_runState = Executing; } @@ -12653,10 +12822,10 @@ namespace Catch { return true; } - auto RunContext::acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { + auto RunContext::acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { using namespace Generators; - GeneratorTracker& tracker = GeneratorTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( "generator", lineInfo ) ); - assert( tracker.isOpen() ); + GeneratorTracker& tracker = GeneratorTracker::acquire(m_trackerContext, + TestCaseTracking::NameAndLocation( static_cast(generatorName), lineInfo ) ); m_lastAssertionInfo.lineInfo = lineInfo; return tracker; } @@ -12699,17 +12868,17 @@ namespace Catch { #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) void RunContext::benchmarkPreparing(std::string const& name) { - m_reporter->benchmarkPreparing(name); - } + m_reporter->benchmarkPreparing(name); + } void RunContext::benchmarkStarting( BenchmarkInfo const& info ) { m_reporter->benchmarkStarting( info ); } void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) { m_reporter->benchmarkEnded( stats ); } - void RunContext::benchmarkFailed(std::string const & error) { - m_reporter->benchmarkFailed(error); - } + void RunContext::benchmarkFailed(std::string const & error) { + m_reporter->benchmarkFailed(error); + } #endif // CATCH_CONFIG_ENABLE_BENCHMARKING void RunContext::pushScopedMessage(MessageInfo const & message) { @@ -12843,9 +13012,8 @@ namespace Catch { } void RunContext::invokeActiveTestCase() { - FatalConditionHandler fatalConditionHandler; // Handle signals + FatalConditionHandlerGuard _(&m_fatalConditionhandler); m_activeTestCase->invoke(); - fatalConditionHandler.reset(); } void RunContext::handleUnfinishedSections() { @@ -13430,6 +13598,7 @@ namespace Catch { // end catch_singletons.cpp // start catch_startup_exception_registry.cpp +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) namespace Catch { void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept { CATCH_TRY { @@ -13445,6 +13614,7 @@ void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexce } } // end namespace Catch +#endif // end catch_startup_exception_registry.cpp // start catch_stream.cpp @@ -13629,7 +13799,7 @@ namespace Catch { namespace { char toLowerCh(char c) { - return static_cast( std::tolower( c ) ); + return static_cast( std::tolower( static_cast(c) ) ); } } @@ -14012,24 +14182,28 @@ namespace Catch { namespace { struct TestHasher { - explicit TestHasher(Catch::SimplePcg32& rng) { - basis = rng(); - basis <<= 32; - basis |= rng(); - } + using hash_t = uint64_t; - uint64_t basis; + explicit TestHasher( hash_t hashSuffix ): + m_hashSuffix{ hashSuffix } {} - uint64_t operator()(TestCase const& t) const { - // Modified FNV-1a hash - static constexpr uint64_t prime = 1099511628211; - uint64_t hash = basis; - for (const char c : t.name) { + uint32_t operator()( TestCase const& t ) const { + // FNV-1a hash with multiplication fold. + const hash_t prime = 1099511628211u; + hash_t hash = 14695981039346656037u; + for ( const char c : t.name ) { hash ^= c; hash *= prime; } - return hash; + hash ^= m_hashSuffix; + hash *= prime; + const uint32_t low{ static_cast( hash ) }; + const uint32_t high{ static_cast( hash >> 32 ) }; + return low * high; } + + private: + hash_t m_hashSuffix; }; } // end unnamed namespace @@ -14047,9 +14221,9 @@ namespace Catch { case RunTests::InRandomOrder: { seedRng( config ); - TestHasher h( rng() ); + TestHasher h{ config.rngSeed() }; - using hashedTest = std::pair; + using hashedTest = std::pair; std::vector indexed_tests; indexed_tests.reserve( unsortedTestCases.size() ); @@ -14212,15 +14386,12 @@ namespace TestCaseTracking { m_currentTracker = tracker; } - TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) - : m_nameAndLocation( nameAndLocation ), + TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ): + ITracker(nameAndLocation), m_ctx( ctx ), m_parent( parent ) {} - NameAndLocation const& TrackerBase::nameAndLocation() const { - return m_nameAndLocation; - } bool TrackerBase::isComplete() const { return m_runState == CompletedSuccessfully || m_runState == Failed; } @@ -14336,7 +14507,8 @@ namespace TestCaseTracking { bool SectionTracker::isComplete() const { bool complete = true; - if ((m_filters.empty() || m_filters[0] == "") + if (m_filters.empty() + || m_filters[0] == "" || std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) { complete = TrackerBase::isComplete(); } @@ -14381,6 +14553,14 @@ namespace TestCaseTracking { m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() ); } + std::vector const& SectionTracker::getFilters() const { + return m_filters; + } + + std::string const& SectionTracker::trimmedName() const { + return m_trimmed_name; + } + } // namespace TestCaseTracking using TestCaseTracking::ITracker; @@ -15115,11 +15295,48 @@ namespace Catch { // end catch_totals.cpp // start catch_uncaught_exceptions.cpp +// start catch_config_uncaught_exceptions.hpp + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +#ifndef CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP +#define CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP + +#if defined(_MSC_VER) +# if _MSC_VER >= 1900 // Visual Studio 2015 or newer +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +# endif +#endif + +#include + +#if defined(__cpp_lib_uncaught_exceptions) \ + && !defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) + +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif // __cpp_lib_uncaught_exceptions + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) \ + && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) \ + && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) + +# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif + +#endif // CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP +// end catch_config_uncaught_exceptions.hpp #include namespace Catch { bool uncaught_exceptions() { -#if defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + return false; +#elif defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) return std::uncaught_exceptions() > 0; #else return std::uncaught_exception(); @@ -15159,7 +15376,7 @@ namespace Catch { } Version const& libraryVersion() { - static Version version( 2, 12, 1, "", 0 ); + static Version version( 2, 13, 5, "", 0 ); return version; } @@ -15561,6 +15778,17 @@ namespace Catch { return std::string(buffer); } + bool shouldShowDuration( IConfig const& config, double duration ) { + if ( config.showDurations() == ShowDurations::Always ) { + return true; + } + if ( config.showDurations() == ShowDurations::Never ) { + return false; + } + const double min = config.minDuration(); + return min >= 0 && duration >= min; + } + std::string serializeFilters( std::vector const& container ) { ReusableStringStream oss; bool first = true; @@ -15827,10 +16055,6 @@ class AssertionPrinter { return "Reports test results on a single line, suitable for IDEs"; } - ReporterPreferences CompactReporter::getPreferences() const { - return m_reporterPrefs; - } - void CompactReporter::noMatchingTestCases( std::string const& spec ) { stream << "No test cases matched '" << spec << '\'' << std::endl; } @@ -15857,8 +16081,9 @@ class AssertionPrinter { } void CompactReporter::sectionEnded(SectionStats const& _sectionStats) { - if (m_config->showDurations() == ShowDurations::Always) { - stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; + double dur = _sectionStats.durationInSeconds; + if ( shouldShowDuration( *m_config, dur ) ) { + stream << getFormattedDuration( dur ) << " s: " << _sectionStats.sectionInfo.name << std::endl; } } @@ -16278,8 +16503,9 @@ void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) { stream << "\nNo assertions in test case"; stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; } - if (m_config->showDurations() == ShowDurations::Always) { - stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; + double dur = _sectionStats.durationInSeconds; + if (shouldShowDuration(*m_config, dur)) { + stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << std::endl; } if (m_headerPrinted) { m_headerPrinted = false; @@ -16738,6 +16964,11 @@ namespace Catch { xml.writeAttribute( "name", name ); } xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) ); + // This is not ideal, but it should be enough to mimic gtest's + // junit output. + // Ideally the JUnit reporter would also handle `skipTest` + // events and write those out appropriately. + xml.writeAttribute( "status", "run" ); writeAssertions( sectionNode ); @@ -17172,6 +17403,10 @@ namespace Catch { .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); + m_xml.scopedElement( "OverallResultsCases") + .writeAttribute( "successes", testGroupStats.totals.testCases.passed ) + .writeAttribute( "failures", testGroupStats.totals.testCases.failed ) + .writeAttribute( "expectedFailures", testGroupStats.totals.testCases.failedButOk ); m_xml.endElement(); } @@ -17181,6 +17416,10 @@ namespace Catch { .writeAttribute( "successes", testRunStats.totals.assertions.passed ) .writeAttribute( "failures", testRunStats.totals.assertions.failed ) .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); + m_xml.scopedElement( "OverallResultsCases") + .writeAttribute( "successes", testRunStats.totals.testCases.passed ) + .writeAttribute( "failures", testRunStats.totals.testCases.failed ) + .writeAttribute( "expectedFailures", testRunStats.totals.testCases.failedButOk ); m_xml.endElement(); } diff --git a/test/src/core/connection/TestParseVLESS.cpp b/test/src/core/connection/TestParseVLESS.cpp index 91ecb393..5fb85f9a 100644 --- a/test/src/core/connection/TestParseVLESS.cpp +++ b/test/src/core/connection/TestParseVLESS.cpp @@ -20,4 +20,54 @@ TEST_CASE("Test VLESS URL Parsing") REQUIRE(errMessage.isEmpty()); REQUIRE(alias.toStdString() == "VLESSTCPXTLSSplice"); } + + SECTION("ALPN Parse Test") + { + const static auto url = "vless://24a613c1-de83-4c63-ba73-a9d08c88fec3@qv2ray.net:13432?security=xtls&alpn=h2%2Chttp%2F1.1"; + + const auto result = vless::Deserialize(url, &alias, &errMessage); + + INFO("Parsed: " << QJsonDocument(result).toJson().toStdString()); + REQUIRE(errMessage.isEmpty()); + + const auto alpnField = QJsonIO::GetValue(result, "outbounds", 0, "streamSettings", "xtlsSettings", "alpn"); + REQUIRE(alpnField.isArray()); + + const auto alpnArray = alpnField.toArray(); + REQUIRE(!alpnArray.empty()); + REQUIRE(alpnArray.size() == 2); + + const auto firstALPN = alpnArray.first(); + REQUIRE(firstALPN.isString()); + + const auto firstALPNString = firstALPN.toString(); + REQUIRE(firstALPNString.toStdString() == "h2"); + + const auto lastALPN = alpnArray.last(); + REQUIRE(lastALPN.isString()); + + const auto lastALPNString = lastALPN.toString(); + REQUIRE(lastALPNString.toStdString() == "http/1.1"); + } + + SECTION("gRPC Parse Test") + { + const static auto url = "vless://6d76fa31-8de2-40d4-8fee-6e61339c416f@qv2ray.net:123?type=grpc&security=tls&serviceName=FuckGFW"; + const auto result = vless::Deserialize(url, &alias, &errMessage); + + INFO("Parsed: " << QJsonDocument(result).toJson().toStdString()); + REQUIRE(errMessage.isEmpty()); + + const auto grpcSettings = QJsonIO::GetValue(result, { "outbounds", 0, "streamSettings", "grpcSettings" }); + REQUIRE(grpcSettings.isObject()); + + const auto grpcSettingsObj = grpcSettings.toObject(); + REQUIRE(grpcSettingsObj.contains("serviceName")); + + const auto serviceName = grpcSettingsObj["serviceName"]; + REQUIRE(serviceName.isString()); + + const auto serviceNameString = serviceName.toString(); + REQUIRE(serviceNameString == "FuckGFW"); + } } diff --git a/translations/en_US.ts b/translations/en_US.ts index 1ab2f04d..3eda7cbf 100644 --- a/translations/en_US.ts +++ b/translations/en_US.ts @@ -7,6 +7,18 @@ Form + + Show / Hide + + + + Selector + + + + Strategy + + CertificateItemWidget @@ -113,6 +125,10 @@ Form + + Port + + ChainWidget @@ -358,19 +374,31 @@ This entry is ignored by V2Ray core when using DoH servers. - FakeDNS + Disable Cache - IP Pool + Query Strategy - Pool Size + Enabled - Disable Cache + Disable Fallback + + + + Miscellaneous + + + + Fake DNS IP Pool + + + + Fake DNS Pool Size @@ -647,11 +675,15 @@ This entry is ignored by V2Ray core when using DoH servers. - Destination Override: + Metadata Only - Metadata Only + FakeDNS-Others + + + + Destination Override @@ -794,10 +826,6 @@ This entry is ignored by V2Ray core when using DoH servers. Log - - Clear log - - Not Connected @@ -1116,6 +1144,10 @@ This entry is ignored by V2Ray core when using DoH servers. Disable Bypassing CN Mainland + + Clear log + + OutboundEditor @@ -1628,10 +1660,6 @@ Custom DNS Settings Invalid inbound listening address. - - Invalid tproxy listening ivp4 address. - - Invalid tproxy listening ipv6 address. @@ -1932,6 +1960,10 @@ If you insist to proceed, we're not providing with any support. FakeDNS + + Invalid tproxy listening ipv4 address. + + QObject @@ -2741,6 +2773,30 @@ Maybe you have downloaded the wrong core? Reference Connection + + Misc + + + + Browser Forwarder + + + + Listening Address + + + + :Listening Port + + + + Observatory + + + + Subject Selector + + RouteSettingsMatrix @@ -2986,33 +3042,6 @@ Maybe you have downloaded the wrong core? - - SpeedWidget - - Proxy ↑ - - - - Proxy ↓ - - - - Direct ↑ - - - - Direct ↓ - - - - Total ↑ - - - - Total ↓ - - - StreamSettingsWidget @@ -3112,7 +3141,7 @@ Maybe you have downloaded the wrong core? - Disable Session Resumption + Enable Session Resumption @@ -3184,7 +3213,11 @@ Maybe you have downloaded the wrong core? - grpc + Max Early Data + + + + Browser Forwarding @@ -3746,4 +3779,19 @@ Maybe you have downloaded the wrong core? + + SpeedWidget + + Proxy + + + + Direct + + + + Total + + + diff --git a/translations/ja_JP.ts b/translations/ja_JP.ts index 1e5e8f44..179d19ac 100644 --- a/translations/ja_JP.ts +++ b/translations/ja_JP.ts @@ -7,6 +7,18 @@ Form Form + + Show / Hide + 表示 / 非表示 + + + Selector + 選択 + + + Strategy + ルール + CertificateItemWidget @@ -119,6 +131,10 @@ Form Form + + Port + ポート + ChainWidget @@ -365,20 +381,32 @@ This entry is ignored by V2Ray core when using DoH servers. このエントリはDoHサーバを使用する場合、V2Rayコアでは無視されます。 - FakeDNS - FakeDNS + Disable Cache + キャッシュを無効化 - IP Pool - IP プール + Query Strategy + クエリ戦略 - Pool Size - プールサイズ + Enabled + 有効 - Disable Cache - キャッシュを無効化 + Disable Fallback + フォールバックを無効化 + + + Miscellaneous + その他 + + + Fake DNS IP Pool + 偽の DNS IP プール + + + Fake DNS Pool Size + 偽の DNS プールサイズ @@ -653,14 +681,18 @@ This entry is ignored by V2Ray core when using DoH servers. FakeDNS FakeDNS - - Destination Override: - 目標上書き: - Metadata Only メタデータのみ + + FakeDNS-Others + FakeDNS-その他 + + + Destination Override + 目標上書き + InboundOutboundWidget @@ -801,10 +833,6 @@ This entry is ignored by V2Ray core when using DoH servers. Log ログ - - Clear log - ログをクリア - Not Connected 接続されていません @@ -1121,6 +1149,10 @@ This entry is ignored by V2Ray core when using DoH servers. Disable Bypassing CN Mainland CN本土バイパスを無効にする + + Clear log + ログをクリア + OutboundEditor @@ -1223,14 +1255,6 @@ This entry is ignored by V2Ray core when using DoH servers. Plugin does not have settings widget. プラグインには設定ウィジェットがありません。 - - Disabling a plugin - プラグインを無効にする - - - This plugin will keep loaded until the next time Qv2ray starts. - このプラグインは、次回Qv2rayが起動するまでロードされたままになります。 - Plugin not loaded プラグインがロードされていない @@ -1648,10 +1672,6 @@ Custom DNS Settings Invalid inbound listening address. 無効なインバウンドリスニングアドレス。 - - Invalid tproxy listening ivp4 address. - 無効なtProxyリスニングIPv4アドレス。 - Invalid tproxy listening ipv6 address. 無効なtProxyリスニングIPv6アドレス。 @@ -1960,6 +1980,10 @@ V2Rayコアのファイル名は通常'v2ray'または'v2ray.exe& FakeDNS FakeDNS + + Invalid tproxy listening ipv4 address. + 無効な tproxy listening ipv4 アドレスです。 + QObject @@ -2576,22 +2600,6 @@ Maybe you have downloaded the wrong core? Route Editor ルートエディター - - Add outbound - アウトバウンドを追加 - - - Add default inbound from global config - グローバル設定からデフォルトのインバウンドを追加 - - - Add blackhole outbound - ブラックホールアウトバウンドを追加 - - - Add Freedom outbound - フリーアウトバウンドを追加 - Protocol プロトコル @@ -2736,6 +2744,78 @@ Maybe you have downloaded the wrong core? (All Connections) (すべての接続) + + Add Inbound + インバウンドを追加 + + + Add Inbound from Global Settings + グローバル設定からインバウンドを追加 + + + Import Outbound + アウトバウンドをインポート + + + Add Outbound + アウトバウンドを追加 + + + Add Blackhole Outbound + ブラックホールアウトバウンドを追加 + + + Add Freedom Outbound + 自由のアウトバウンドを追加 + + + Add Balancer + バランサーを追加 + + + Add Proxy Chain + プロキシチェーンを追加 + + + Group + グループ + + + Connection + 接続 + + + Copy Connection + 接続をコピー + + + Reference Connection + 参照接続 + + + Misc + その他 + + + Browser Forwarder + ブラウザフォワーダー + + + Listening Address + リスニングアドレス + + + :Listening Port + :リッスンポート + + + Observatory + Observatory + + + Subject Selector + 件名の選択 + RouteSettingsMatrix @@ -2982,33 +3062,6 @@ Maybe you have downloaded the wrong core? FG - - SpeedWidget - - Proxy ↑ - プロキシ↑ - - - Proxy ↓ - プロキシ↓ - - - Direct ↑ - 直接↑ - - - Direct ↓ - 直接↓ - - - Total ↑ - 合計↑ - - - Total ↓ - 合計↓ - - StreamSettingsWidget @@ -3108,8 +3161,8 @@ Maybe you have downloaded the wrong core? tProxy モード - Disable Session Resumption - セッション再開を無効にする + Enable Session Resumption + セッション再開を有効にする Security Type @@ -3180,8 +3233,12 @@ Maybe you have downloaded the wrong core? サービス名 - grpc - grpc + Max Early Data + 初期データの最大数 + + + Browser Forwarding + ブラウザ転送 @@ -3739,4 +3796,26 @@ Maybe you have downloaded the wrong core? 受信タグ + + MainWindowWidget + + Qv2ray Utilities + Qv2ray ユーティリティ + + + + SpeedWidget + + Proxy + プロキシで接続 + + + Direct + 直接接続 + + + Total + 合計 + + diff --git a/translations/yue.ts b/translations/yue.ts index f5ed15f2..47de5865 100644 --- a/translations/yue.ts +++ b/translations/yue.ts @@ -7,6 +7,18 @@ Form 窗口 + + Show / Hide + Show / Hide + + + Selector + Selector + + + Strategy + Strategy + CertificateItemWidget @@ -119,6 +131,10 @@ Form 窗口 + + Port + 端口 + ChainWidget @@ -368,20 +384,32 @@ This entry is ignored by V2Ray core when using DoH servers. 喺使用DoH服務器嘅時候,V2Ray核心將忽略此條目。 - FakeDNS - FakeDNS + Disable Cache + Disable Cache - IP Pool - IP Pool + Query Strategy + Query Strategy - Pool Size - Pool Size + Enabled + Enabled - Disable Cache - Disable Cache + Disable Fallback + Disable Fallback + + + Miscellaneous + Miscellaneous + + + Fake DNS IP Pool + Fake DNS IP Pool + + + Fake DNS Pool Size + Fake DNS Pool Size @@ -656,14 +684,18 @@ This entry is ignored by V2Ray core when using DoH servers. FakeDNS FakeDNS - - Destination Override: - Destination Override: - Metadata Only Metadata Only + + FakeDNS-Others + FakeDNS-Others + + + Destination Override + Destination Override + InboundOutboundWidget @@ -804,10 +836,6 @@ This entry is ignored by V2Ray core when using DoH servers. Log Log - - Clear log - Clear log - Not Connected Not Connected @@ -1132,6 +1160,10 @@ This entry is ignored by V2Ray core when using DoH servers. Disable Bypassing CN Mainland Disable Bypassing CN Mainland + + Clear log + Clear log + OutboundEditor @@ -1234,14 +1266,6 @@ This entry is ignored by V2Ray core when using DoH servers. Plugin does not have settings widget. Plugin does not have settings widget. - - Disabling a plugin - Disabling a plugin - - - This plugin will keep loaded until the next time Qv2ray starts. - This plugin will keep loaded until the next time Qv2ray starts. - Plugin not loaded Plugin not loaded @@ -1659,10 +1683,6 @@ Custom DNS Settings Invalid inbound listening address. Invalid inbound listening address. - - Invalid tproxy listening ivp4 address. - Invalid tproxy listening ivp4 address. - Invalid tproxy listening ipv6 address. Invalid tproxy listening ipv6 address. @@ -1971,6 +1991,10 @@ If you insist to proceed, we're not providing with any support.FakeDNS FakeDNS + + Invalid tproxy listening ipv4 address. + Invalid tproxy listening ipv4 address. + QObject @@ -2593,22 +2617,6 @@ Maybe you have downloaded the wrong core? Route Editor Route Editor - - Add outbound - Add outbound - - - Add default inbound from global config - Add default inbound from global config - - - Add blackhole outbound - Add blackhole outbound - - - Add Freedom outbound - Add Freedom outbound - Protocol 协定 @@ -2753,6 +2761,78 @@ Maybe you have downloaded the wrong core? (All Connections) (All Connections) + + Add Inbound + Add Inbound + + + Add Inbound from Global Settings + Add Inbound from Global Settings + + + Import Outbound + Import Outbound + + + Add Outbound + Add Outbound + + + Add Blackhole Outbound + Add Blackhole Outbound + + + Add Freedom Outbound + Add Freedom Outbound + + + Add Balancer + Add Balancer + + + Add Proxy Chain + Add Proxy Chain + + + Group + 分组 + + + Connection + Connection + + + Copy Connection + Copy Connection + + + Reference Connection + Reference Connection + + + Misc + Misc + + + Browser Forwarder + Browser Forwarder + + + Listening Address + Listening Address + + + :Listening Port + :Listening Port + + + Observatory + Observatory + + + Subject Selector + Subject Selector + RouteSettingsMatrix @@ -2999,33 +3079,6 @@ Maybe you have downloaded the wrong core? FG - - SpeedWidget - - Proxy ↑ - Proxy ↑ - - - Proxy ↓ - Proxy ↓ - - - Direct ↑ - Direct ↑ - - - Direct ↓ - Direct ↓ - - - Total ↑ - Total ↑ - - - Total ↓ - Total ↓ - - StreamSettingsWidget @@ -3125,8 +3178,8 @@ Maybe you have downloaded the wrong core? tProxy Mode - Disable Session Resumption - Disable Session Resumption + Enable Session Resumption + Enable Session Resumption Security Type @@ -3197,8 +3250,12 @@ Maybe you have downloaded the wrong core? Service Name - grpc - grpc + Max Early Data + Max Early Data + + + Browser Forwarding + Browser Forwarding @@ -3756,4 +3813,26 @@ Maybe you have downloaded the wrong core? Inbound Tag + + MainWindowWidget + + Qv2ray Utilities + Qv2ray Utilities + + + + SpeedWidget + + Proxy + Proxy + + + Direct + Direct + + + Total + Total + + diff --git a/translations/zh_CN.ts b/translations/zh_CN.ts index 83d33e5b..f7c7ea2d 100644 --- a/translations/zh_CN.ts +++ b/translations/zh_CN.ts @@ -7,6 +7,18 @@ Form 窗体 + + Show / Hide + 显示/隐藏 + + + Selector + 选择器 + + + Strategy + 策略 + CertificateItemWidget @@ -119,6 +131,10 @@ Form 窗体 + + Port + 端口 + ChainWidget @@ -365,20 +381,32 @@ This entry is ignored by V2Ray core when using DoH servers. 当使用 DoH 模式时,此项无效。 - FakeDNS - FakeDNS + Disable Cache + 禁用缓存 - IP Pool - IP地址库 + Query Strategy + 查询策略 - Pool Size - 池大小 + Enabled + 已启用 - Disable Cache - 禁用缓存 + Disable Fallback + 禁用回退查询 + + + Miscellaneous + 杂项 + + + Fake DNS IP Pool + FakeDNS IP 库 + + + Fake DNS Pool Size + FakeDNS 池大小 @@ -653,14 +681,18 @@ This entry is ignored by V2Ray core when using DoH servers. FakeDNS FakeDNS - - Destination Override: - 目标覆盖: - Metadata Only 仅限元数据 + + FakeDNS-Others + FakeDNS-其他 + + + Destination Override + 目标覆盖 + InboundOutboundWidget @@ -801,10 +833,6 @@ This entry is ignored by V2Ray core when using DoH servers. Log 日志 - - Clear log - 清除日志 - Not Connected 未连接 @@ -1121,6 +1149,10 @@ This entry is ignored by V2Ray core when using DoH servers. Disable Bypassing CN Mainland 禁用绕过中国大陆 + + Clear log + 清除日志 + OutboundEditor @@ -1223,14 +1255,6 @@ This entry is ignored by V2Ray core when using DoH servers. Plugin does not have settings widget. 插件无设置组件。 - - Disabling a plugin - 禁用插件 - - - This plugin will keep loaded until the next time Qv2ray starts. - 重启前插件会保持活动。 - Plugin not loaded 插件未加载 @@ -1648,10 +1672,6 @@ Custom DNS Settings Invalid inbound listening address. 入站监听地址不可用。 - - Invalid tproxy listening ivp4 address. - 无效的透明代理 IPv4 监听地址。 - Invalid tproxy listening ipv6 address. 无效的透明代理 IPv6 监听地址。 @@ -1960,6 +1980,10 @@ V2Ray 核心可执行文件的文件名通常是 'v2ray' 或者 ' FakeDNS FakeDNS + + Invalid tproxy listening ipv4 address. + 无效的 tproxy 监听ipv4 地址。 + QObject @@ -2576,22 +2600,6 @@ Maybe you have downloaded the wrong core? Route Editor 路由编辑器 - - Add outbound - 添加出站 - - - Add default inbound from global config - 从全局配置中添加默认的入站设置 - - - Add blackhole outbound - 添加黑洞出站 - - - Add Freedom outbound - 添加自由出站 - Protocol 协议 @@ -2736,6 +2744,78 @@ Maybe you have downloaded the wrong core? (All Connections) (所有连接) + + Add Inbound + 添加入站 + + + Add Inbound from Global Settings + 从全局设置中添加入站 + + + Import Outbound + 导入出站 + + + Add Outbound + 添加出站 + + + Add Blackhole Outbound + 添加黑洞出站 + + + Add Freedom Outbound + 添加自由出站 + + + Add Balancer + 添加平衡器 + + + Add Proxy Chain + 添加代理链接 + + + Group + 分组 + + + Connection + 连接 + + + Copy Connection + 复制连接 + + + Reference Connection + 引用连接 + + + Misc + 其他 + + + Browser Forwarder + 浏览器转发器 + + + Listening Address + 监听地址 + + + :Listening Port + :监听端口 + + + Observatory + 天文台 + + + Subject Selector + 主题选择器 + RouteSettingsMatrix @@ -2982,33 +3062,6 @@ Maybe you have downloaded the wrong core? FG - - SpeedWidget - - Proxy ↑ - 代理↑ - - - Proxy ↓ - 代理↓ - - - Direct ↑ - 直连↑ - - - Direct ↓ - 直连↓ - - - Total ↑ - 总计↑ - - - Total ↓ - 总计↓ - - StreamSettingsWidget @@ -3108,8 +3161,8 @@ Maybe you have downloaded the wrong core? tProxy 工作模式 - Disable Session Resumption - 禁用会话返回 + Enable Session Resumption + 启用会话恢复 Security Type @@ -3173,15 +3226,19 @@ Maybe you have downloaded the wrong core? AllowInsecure is insecure, do not allow insecure. - 允许不安全,不允许不安全。 + AllowInsecure 是不安全的,请勿 AllowInsecure。 Service Name 服务名称 - grpc - 格子 + Max Early Data + 最大早期数据 + + + Browser Forwarding + 浏览器转发中 @@ -3725,7 +3782,7 @@ Maybe you have downloaded the wrong core? VMess MD5 with Non-zero AlterID has been deprecated, please use VMessAEAD. - VMessMD5使用非零变压器ID已废弃,请使用 VMessAEAD + VMess MD5 使用非零 AlterID 已废弃,请使用 VMessAEAD @@ -3739,4 +3796,26 @@ Maybe you have downloaded the wrong core? 入站标签 + + MainWindowWidget + + Qv2ray Utilities + Qv2ray 工具 + + + + SpeedWidget + + Proxy + 代理 + + + Direct + 直连 + + + Total + 总计 + + diff --git a/translations/zh_TW.ts b/translations/zh_TW.ts index 9de92dd7..a30285c9 100644 --- a/translations/zh_TW.ts +++ b/translations/zh_TW.ts @@ -7,6 +7,18 @@ Form 視窗 + + Show / Hide + Show / Hide + + + Selector + Selector + + + Strategy + 策略 + CertificateItemWidget @@ -113,6 +125,10 @@ Form 視窗 + + Port + 端口 + ChainWidget @@ -359,20 +375,32 @@ This entry is ignored by V2Ray core when using DoH servers. 當使用 DoH 模式時,此項無效。 - FakeDNS - FakeDNS + Disable Cache + Disable Cache - IP Pool - IP Pool + Query Strategy + Query Strategy - Pool Size - Pool Size + Enabled + 已啟用 - Disable Cache - Disable Cache + Disable Fallback + Disable Fallback + + + Miscellaneous + Miscellaneous + + + Fake DNS IP Pool + Fake DNS IP Pool + + + Fake DNS Pool Size + Fake DNS Pool Size @@ -647,14 +675,18 @@ This entry is ignored by V2Ray core when using DoH servers. FakeDNS FakeDNS - - Destination Override: - 目標覆蓋: - Metadata Only Metadata Only + + FakeDNS-Others + FakeDNS-Others + + + Destination Override + 目標覆蓋 + InboundOutboundWidget @@ -795,10 +827,6 @@ This entry is ignored by V2Ray core when using DoH servers. Log   紀錄檔 - - Clear log - 清除紀錄檔 - Not Connected 未連接 @@ -1115,6 +1143,10 @@ This entry is ignored by V2Ray core when using DoH servers. Disable Bypassing CN Mainland Disable Bypassing CN Mainland + + Clear log + 清除紀錄檔 + OutboundEditor @@ -1217,14 +1249,6 @@ This entry is ignored by V2Ray core when using DoH servers. Plugin does not have settings widget. 該外掛程式不包含設定 Widget。 - - Disabling a plugin - 禁用外掛程式 - - - This plugin will keep loaded until the next time Qv2ray starts. - 重啟前外掛程式會保持活動。 - Plugin not loaded 外掛程式未載入 @@ -1642,10 +1666,6 @@ Custom DNS Settings Invalid inbound listening address. 入站監聽位址不可用。 - - Invalid tproxy listening ivp4 address. - 無效的透明代理 IPv4 監聽位址。 - Invalid tproxy listening ipv6 address. 無效的透明代理 IPv6 監聽位址。 @@ -1954,6 +1974,10 @@ V2Ray 核心可執行檔的檔案名通常是 'v2ray' 或者 'v2r FakeDNS FakeDNS + + Invalid tproxy listening ipv4 address. + Invalid tproxy listening ipv4 address. + QObject @@ -2570,22 +2594,6 @@ Maybe you have downloaded the wrong core? Route Editor 路由編輯器 - - Add outbound - 添加出站 - - - Add default inbound from global config - 從全域配置中添加預設的入站設置 - - - Add blackhole outbound - 添加黑洞出站 - - - Add Freedom outbound - 添加自由出站 - Protocol 協定 @@ -2730,6 +2738,78 @@ Maybe you have downloaded the wrong core? (All Connections) (All Connections) + + Add Inbound + Add Inbound + + + Add Inbound from Global Settings + Add Inbound from Global Settings + + + Import Outbound + Import Outbound + + + Add Outbound + Add Outbound + + + Add Blackhole Outbound + Add Blackhole Outbound + + + Add Freedom Outbound + Add Freedom Outbound + + + Add Balancer + Add Balancer + + + Add Proxy Chain + Add Proxy Chain + + + Group + 分組 + + + Connection + Connection + + + Copy Connection + Copy Connection + + + Reference Connection + Reference Connection + + + Misc + Misc + + + Browser Forwarder + Browser Forwarder + + + Listening Address + 監聽位址 + + + :Listening Port + :Listening Port + + + Observatory + Observatory + + + Subject Selector + Subject Selector + RouteSettingsMatrix @@ -2976,33 +3056,6 @@ Maybe you have downloaded the wrong core? FG - - SpeedWidget - - Proxy ↑ - 代理↑ - - - Proxy ↓ - 代理↓ - - - Direct ↑ - 直連↑ - - - Direct ↓ - 直連↓ - - - Total ↑ - 總計↑ - - - Total ↓ - 總計↓ - - StreamSettingsWidget @@ -3102,8 +3155,8 @@ Maybe you have downloaded the wrong core? tProxy 工作模式 - Disable Session Resumption - 禁用會話返回 + Enable Session Resumption + Enable Session Resumption Security Type @@ -3174,8 +3227,12 @@ Maybe you have downloaded the wrong core? Service Name - grpc - grpc + Max Early Data + Max Early Data + + + Browser Forwarding + Browser Forwarding @@ -3733,4 +3790,26 @@ Maybe you have downloaded the wrong core? Inbound Tag + + MainWindowWidget + + Qv2ray Utilities + Qv2ray Utilities + + + + SpeedWidget + + Proxy + 代理 + + + Direct + 直連 + + + Total + Total + +