Commit 2c2c8a69 authored by Kenton Varda's avatar Kenton Varda Committed by GitHub

Merge pull request #464 from sandstorm-io/appveyor-tests

Have AppVeyor run the tests.
parents 4c169a09 6fb5003c
...@@ -7,9 +7,8 @@ ...@@ -7,9 +7,8 @@
# support for. # support for.
# - Use CMake to ... # - Use CMake to ...
# build Cap'n Proto with MinGW. # build Cap'n Proto with MinGW.
# build Cap'n Proto with VS2015 and the MinGW-built capnp tools. # build Cap'n Proto with VS2015.
# build the Cap'n Proto samples with VS2015 and the MinGW-built capnp tools. # build Cap'n Proto samples with VS2015.
# - Archive both Cap'n Proto builds in capnproto-c++-{mingw,vs2015}.zip artifacts.
version: "{build}" version: "{build}"
...@@ -52,29 +51,23 @@ build_script: ...@@ -52,29 +51,23 @@ build_script:
-DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DCMAKE_BUILD_TYPE=%BUILD_TYPE%
-DCMAKE_INSTALL_PREFIX=%INSTALL_PREFIX_MINGW% -DCMAKE_INSTALL_PREFIX=%INSTALL_PREFIX_MINGW%
- cmake --build build-mingw --target install -- -j%NUMBER_OF_PROCESSORS% - cmake --build build-mingw --target install -- -j%NUMBER_OF_PROCESSORS%
- set PATH=%INSTALL_PREFIX_MINGW%\bin;%PATH%
- echo "Building Cap'n Proto with Visual Studio 2015" - echo "Building Cap'n Proto with Visual Studio 2015"
- >- - >-
cmake -Hc++ -Bbuild-vs2015 -G "Visual Studio 14 2015" -A x64 cmake -Hc++ -Bbuild-vs2015 -G "Visual Studio 14 2015" -A x64
-DEXTERNAL_CAPNP=ON
-DCMAKE_INSTALL_PREFIX=%INSTALL_PREFIX_VS2015% -DCMAKE_INSTALL_PREFIX=%INSTALL_PREFIX_VS2015%
- cmake --build build-vs2015 --config %BUILD_TYPE% --target install - cmake --build build-vs2015 --config %BUILD_TYPE% --target install
# TODO(someday): pass `-- /maxcpucount` for a parallel build. Right now it occasionally expresses # TODO(someday): pass `-- /maxcpucount` for a parallel build. Right now it occasionally expresses
# a filesystem-related race: capnp-capnpc++ complains that it can't create test.capnp.h. # a filesystem-related race: capnp-capnpc++ complains that it can't create test.capnp.h.
- echo "Building Cap'n Proto samples with Visual Studio 2015"
- >- - >-
cmake -Hc++/samples -Bbuild-samples -G "Visual Studio 14 2015" -A x64 cmake -Hc++/samples -Bbuild-vs2015-samples -G "Visual Studio 14 2015" -A x64
-DCMAKE_PREFIX_PATH=%INSTALL_PREFIX_VS2015% -DCMAKE_PREFIX_PATH=%INSTALL_PREFIX_VS2015%
-DCAPNP_EXECUTABLE=%INSTALL_PREFIX_MINGW%\bin\capnp.exe - cmake --build build-vs2015-samples --config %BUILD_TYPE%
-DCAPNPC_CXX_EXECUTABLE=%INSTALL_PREFIX_MINGW%\bin\capnpc-c++.exe
- cmake --build build-samples --config %BUILD_TYPE%
# TODO(soon): Fix tests on Windows. test_script:
# - timeout /t 2
# Tests are not currently run, because until the tests start passing normally, it's more useful for # Sleep a little to prevent interleaving test output with build output.
# us to only be notified of build errors. When they start passing, uncomment the code below. - cd build-vs2015\src
# - ctest -V -C %BUILD_TYPE%
#test_script:
# - timeout /t 2
# # Sleep a little to prevent interleaving test output with build output.
# - cd build-msvc\src
# - ctest -V -C %BUILD_TYPE%
...@@ -29,31 +29,11 @@ option(BUILD_TESTING "Build unit tests and enable CTest 'check' target." ON) ...@@ -29,31 +29,11 @@ option(BUILD_TESTING "Build unit tests and enable CTest 'check' target." ON)
option(EXTERNAL_CAPNP "Use the system capnp binary, or the one specified in $CAPNP, instead of using the compiled one." OFF) option(EXTERNAL_CAPNP "Use the system capnp binary, or the one specified in $CAPNP, instead of using the compiled one." OFF)
option(CAPNP_LITE "Compile Cap'n Proto in 'lite mode', in which all reflection APIs (schema.h, dynamic.h, etc.) are not included. Produces a smaller library at the cost of features. All programs built against the library must be compiled with -DCAPNP_LITE. Requires EXTERNAL_CAPNP." OFF) option(CAPNP_LITE "Compile Cap'n Proto in 'lite mode', in which all reflection APIs (schema.h, dynamic.h, etc.) are not included. Produces a smaller library at the cost of features. All programs built against the library must be compiled with -DCAPNP_LITE. Requires EXTERNAL_CAPNP." OFF)
if(MSVC)
option(CAPNP_BUILD_TOOLS "Build Cap'n Proto tools" OFF)
else()
option(CAPNP_BUILD_TOOLS "Build Cap'n Proto tools" ON)
endif()
mark_as_advanced(CAPNP_BUILD_TOOLS)
# TODO(msvc): This option exists purely to ease the MSVC port. As of this writing, the latest
# stable version of Visual C++ (VS2015U3) cannot build the Cap'n Proto tools, so this option
# defaults to OFF in that case. In all other cases it defaults to ON.
# Check for invalid combinations of build options # Check for invalid combinations of build options
if(CAPNP_LITE AND BUILD_TESTING AND NOT EXTERNAL_CAPNP) if(CAPNP_LITE AND BUILD_TESTING AND NOT EXTERNAL_CAPNP)
message(SEND_ERROR "You must set EXTERNAL_CAPNP when using CAPNP_LITE and BUILD_TESTING.") message(SEND_ERROR "You must set EXTERNAL_CAPNP when using CAPNP_LITE and BUILD_TESTING.")
endif() endif()
if(NOT CAPNP_BUILD_TOOLS AND BUILD_TESTING AND NOT EXTERNAL_CAPNP)
message(SEND_ERROR "You must set EXTERNAL_CAPNP when using BUILD_TESTING but not CAPNP_BUILD_TOOLS")
if(MSVC)
message(SEND_ERROR "Note: CAPNP_BUILD_TOOLS is off because you are using MSVC, which cannot build the tools")
# If we're using MSVC, then we know the reason why CAPNP_BUILD_TOOLS is off and should help the
# user out. If we're NOT using MSVC, then the user must have explicitly set CAPNP_BUILD_TOOLS
# off, so they're on their own.
endif()
endif()
if(CAPNP_LITE) if(CAPNP_LITE)
set(CAPNP_LITE_FLAG "-DCAPNP_LITE") set(CAPNP_LITE_FLAG "-DCAPNP_LITE")
# This flag is attached as PUBLIC target_compile_definition to kj target # This flag is attached as PUBLIC target_compile_definition to kj target
...@@ -64,6 +44,14 @@ endif() ...@@ -64,6 +44,14 @@ endif()
if(MSVC) if(MSVC)
# TODO(cleanup): Enable higher warning level in MSVC, but make sure to test # TODO(cleanup): Enable higher warning level in MSVC, but make sure to test
# build with that warning level and clean out false positives. # build with that warning level and clean out false positives.
add_compile_options(/wo4503)
# Only warn once on truncated decorated names. The maximum symbol length MSVC
# supports is 4k characters, which the parser framework regularly blows. The
# compiler likes to print out the entire type that went over the limit along
# with this warning, which gets unbearably spammy. That said, we don't want to
# just ignore it, so I'm letting it trigger once until we find some places to
# inject ParserRefs.
else() else()
# Note that it's important to add new CXXFLAGS before ones specified by the # Note that it's important to add new CXXFLAGS before ones specified by the
# user, so that the user's flags override them. This is particularly # user, so that the user's flags override them. This is particularly
......
...@@ -12,10 +12,8 @@ ...@@ -12,10 +12,8 @@
set(CapnProto_VERSION @VERSION@) set(CapnProto_VERSION @VERSION@)
if(@CAPNP_BUILD_TOOLS@) set(CAPNP_EXECUTABLE $<TARGET_FILE:CapnProto::capnp_tool>)
set(CAPNP_EXECUTABLE $<TARGET_FILE:CapnProto::capnp_tool>) set(CAPNPC_CXX_EXECUTABLE $<TARGET_FILE:CapnProto::capnpc_cpp>)
set(CAPNPC_CXX_EXECUTABLE $<TARGET_FILE:CapnProto::capnpc_cpp>)
endif()
set(CAPNP_INCLUDE_DIRECTORY "@PACKAGE_CMAKE_INSTALL_FULL_INCLUDEDIR@") set(CAPNP_INCLUDE_DIRECTORY "@PACKAGE_CMAKE_INSTALL_FULL_INCLUDEDIR@")
# work around http://public.kitware.com/Bug/view.php?id=15258 # work around http://public.kitware.com/Bug/view.php?id=15258
......
...@@ -124,60 +124,58 @@ endif() ...@@ -124,60 +124,58 @@ endif()
# Tools/Compilers ============================================================== # Tools/Compilers ==============================================================
if(CAPNP_BUILD_TOOLS) set(capnpc_sources
set(capnpc_sources compiler/md5.c++
compiler/md5.c++ compiler/error-reporter.c++
compiler/error-reporter.c++ compiler/lexer.capnp.c++
compiler/lexer.capnp.c++ compiler/lexer.c++
compiler/lexer.c++ compiler/grammar.capnp.c++
compiler/grammar.capnp.c++ compiler/parser.c++
compiler/parser.c++ compiler/node-translator.c++
compiler/node-translator.c++ compiler/compiler.c++
compiler/compiler.c++ schema-parser.c++
schema-parser.c++ serialize-text.c++
serialize-text.c++ )
) if(NOT CAPNP_LITE)
if(NOT CAPNP_LITE) add_library(capnpc ${capnpc_sources})
add_library(capnpc ${capnpc_sources}) target_link_libraries(capnpc capnp kj)
target_link_libraries(capnpc capnp kj) install(TARGETS capnpc ${INSTALL_TARGETS_DEFAULT_ARGS})
install(TARGETS capnpc ${INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES ${capnpc_headers} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/capnp")
install(FILES ${capnpc_headers} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/capnp") endif()
endif()
if(NOT CAPNP_LITE) if(NOT CAPNP_LITE)
add_executable(capnp_tool add_executable(capnp_tool
compiler/module-loader.c++ compiler/module-loader.c++
compiler/capnp.c++ compiler/capnp.c++
) )
target_link_libraries(capnp_tool capnpc capnp kj) target_link_libraries(capnp_tool capnpc capnp kj)
set_target_properties(capnp_tool PROPERTIES OUTPUT_NAME capnp) set_target_properties(capnp_tool PROPERTIES OUTPUT_NAME capnp)
set_target_properties(capnp_tool PROPERTIES CAPNP_INCLUDE_DIRECTORY set_target_properties(capnp_tool PROPERTIES CAPNP_INCLUDE_DIRECTORY
$<JOIN:$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>,$<INSTALL_INTERFACE:${CMAKE_INSTALL_BINDIR}/..>> $<JOIN:$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>,$<INSTALL_INTERFACE:${CMAKE_INSTALL_BINDIR}/..>>
) )
add_executable(capnpc_cpp add_executable(capnpc_cpp
compiler/capnpc-c++.c++ compiler/capnpc-c++.c++
) )
target_link_libraries(capnpc_cpp capnp kj) target_link_libraries(capnpc_cpp capnp kj)
set_target_properties(capnpc_cpp PROPERTIES OUTPUT_NAME capnpc-c++) set_target_properties(capnpc_cpp PROPERTIES OUTPUT_NAME capnpc-c++)
#Capnp tool needs capnpc_cpp location. But cmake deprecated LOCATION property. #Capnp tool needs capnpc_cpp location. But cmake deprecated LOCATION property.
#So we use custom property to pass location #So we use custom property to pass location
set_target_properties(capnpc_cpp PROPERTIES CAPNPC_CXX_EXECUTABLE set_target_properties(capnpc_cpp PROPERTIES CAPNPC_CXX_EXECUTABLE
$<JOIN:$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/capnpc-c++>,$<INSTALL_INTERFACE:${CMAKE_INSTALL_BINDIR}/capnpc-c++>> $<TARGET_FILE:capnpc_cpp>
) )
add_executable(capnpc_capnp add_executable(capnpc_capnp
compiler/capnpc-capnp.c++ compiler/capnpc-capnp.c++
) )
target_link_libraries(capnpc_capnp capnp kj) target_link_libraries(capnpc_capnp capnp kj)
set_target_properties(capnpc_capnp PROPERTIES OUTPUT_NAME capnpc-capnp) set_target_properties(capnpc_capnp PROPERTIES OUTPUT_NAME capnpc-capnp)
install(TARGETS capnp_tool capnpc_cpp capnpc_capnp ${INSTALL_TARGETS_DEFAULT_ARGS}) install(TARGETS capnp_tool capnpc_cpp capnpc_capnp ${INSTALL_TARGETS_DEFAULT_ARGS})
# Symlink capnpc -> capnp # Symlink capnpc -> capnp
install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E create_symlink capnp \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_BINDIR}/capnpc\")") install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E create_symlink capnp \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_BINDIR}/capnpc\")")
endif() # NOT CAPNP_LITE endif() # NOT CAPNP_LITE
endif() # CAPNP_BUILD_TOOLS
# Tests ======================================================================== # Tests ========================================================================
...@@ -195,12 +193,10 @@ if(BUILD_TESTING) ...@@ -195,12 +193,10 @@ if(BUILD_TESTING)
capnp_generate_cpp(test_capnp_cpp_files test_capnp_h_files ${test_capnp_files}) capnp_generate_cpp(test_capnp_cpp_files test_capnp_h_files ${test_capnp_files})
set(test_libraries capnp kj-test kj) if(CAPNP_LITE)
if(NOT CAPNP_LITE) set(test_libraries capnp kj-test kj)
list(APPEND test_libraries capnp-json capnp-rpc kj-async) else()
endif() set(test_libraries capnp-json capnp-rpc capnp capnpc kj-async kj-test kj)
if(CAPNP_BUILD_TOOLS)
list(APPEND test_libraries capnpc)
endif() endif()
add_executable(capnp-tests add_executable(capnp-tests
...@@ -232,12 +228,16 @@ if(BUILD_TESTING) ...@@ -232,12 +228,16 @@ if(BUILD_TESTING)
membrane-test.c++ membrane-test.c++
schema-test.c++ schema-test.c++
schema-loader-test.c++ schema-loader-test.c++
schema-parser-test.c++
dynamic-test.c++ dynamic-test.c++
stringify-test.c++ stringify-test.c++
serialize-async-test.c++ serialize-async-test.c++
serialize-text-test.c++
rpc-test.c++ rpc-test.c++
rpc-twoparty-test.c++ rpc-twoparty-test.c++
ez-rpc-test.c++ ez-rpc-test.c++
compiler/lexer-test.c++
compiler/md5-test.c++
test-util.c++ test-util.c++
compat/json-test.c++ compat/json-test.c++
${test_capnp_cpp_files} ${test_capnp_cpp_files}
...@@ -253,29 +253,9 @@ if(BUILD_TESTING) ...@@ -253,29 +253,9 @@ if(BUILD_TESTING)
add_dependencies(check capnp-heavy-tests) add_dependencies(check capnp-heavy-tests)
add_test(NAME capnp-heavy-tests-run COMMAND capnp-heavy-tests) add_test(NAME capnp-heavy-tests-run COMMAND capnp-heavy-tests)
if(CAPNP_BUILD_TOOLS) add_executable(capnp-evolution-tests compiler/evolution-test.c++)
add_executable(capnp-parse-tests target_link_libraries(capnp-evolution-tests capnpc capnp kj)
schema-parser-test.c++ add_dependencies(check capnp-evolution-tests)
serialize-text-test.c++ add_test(NAME capnp-evolution-tests-run COMMAND capnp-evolution-tests)
compiler/md5-test.c++
compiler/lexer-test.c++
test-util.c++
${test_capnp_cpp_files}
${test_capnp_h_files}
)
target_link_libraries(capnp-parse-tests ${test_libraries})
if(NOT MSVC)
set_target_properties(capnp-parse-tests
PROPERTIES COMPILE_FLAGS "-Wno-deprecated-declarations"
)
endif()
add_dependencies(check capnp-parse-tests)
add_test(NAME capnp-parse-tests-run COMMAND capnp-parse-tests)
add_executable(capnp-evolution-tests compiler/evolution-test.c++)
target_link_libraries(capnp-evolution-tests capnpc capnp kj)
add_dependencies(check capnp-evolution-tests)
add_test(NAME capnp-evolution-tests-run COMMAND capnp-evolution-tests)
endif() # CAPNP_BUILD_TOOLS
endif() # NOT CAPNP_LITE endif() # NOT CAPNP_LITE
endif() # BUILD_TESTING endif() # BUILD_TESTING
...@@ -57,7 +57,7 @@ protected: ...@@ -57,7 +57,7 @@ protected:
} }
}; };
constexpr MmapDisposer mmapDisposer = MmapDisposer(); KJ_CONSTEXPR(static const) MmapDisposer mmapDisposer = MmapDisposer();
kj::Array<const byte> mmapForRead(kj::StringPtr filename) { kj::Array<const byte> mmapForRead(kj::StringPtr filename) {
int fd; int fd;
......
...@@ -1703,7 +1703,7 @@ TEST(Encoding, Embeds) { ...@@ -1703,7 +1703,7 @@ TEST(Encoding, Embeds) {
auto root = builder.getRoot<TestAllTypes>(); auto root = builder.getRoot<TestAllTypes>();
initTestMessage(root); initTestMessage(root);
kj::StringPtr text = test::EMBEDDED_TEXT; kj::StringPtr text = test::EMBEDDED_TEXT;
EXPECT_EQ(kj::str(root, '\n').size(), text.size()); EXPECT_EQ(kj::str(root, text.endsWith("\r\n") ? "\r\n" : "\n"), text);
} }
#endif // CAPNP_LITE #endif // CAPNP_LITE
......
...@@ -19,6 +19,10 @@ ...@@ -19,6 +19,10 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE. // THE SOFTWARE.
#if !_MSC_VER
// (MSVC targets only little-endian platforms and so we haven't implemented any byte swapping
// intrinsics for it. So, this test would fail there.)
// Test that the code for the opposite endianness of our CPU works. E.g. on x86 this will test // Test that the code for the opposite endianness of our CPU works. E.g. on x86 this will test
// the bswap-based code. // the bswap-based code.
#define CAPNP_REVERSE_ENDIAN 1 #define CAPNP_REVERSE_ENDIAN 1
...@@ -104,3 +108,5 @@ TEST(EndianReverse, EightBytes) { ...@@ -104,3 +108,5 @@ TEST(EndianReverse, EightBytes) {
} // namespace } // namespace
} // namespace _ (private) } // namespace _ (private)
} // namespace capnp } // namespace capnp
#endif // !_MSC_VER
...@@ -28,12 +28,6 @@ ...@@ -28,12 +28,6 @@
#include <kj/miniposix.h> #include <kj/miniposix.h>
#include "test-util.h" #include "test-util.h"
#if !_WIN32
// This test is super-slow on Windows seemingly due to generating exception stack traces being
// expensive.
//
// TODO(perf): Maybe create an API to disable stack traces, and use it here.
namespace capnp { namespace capnp {
namespace _ { // private namespace _ { // private
namespace { namespace {
...@@ -47,6 +41,16 @@ bool skipFuzzTest() { ...@@ -47,6 +41,16 @@ bool skipFuzzTest() {
} }
} }
class DisableStackTraces: public kj::ExceptionCallback {
// This test generates a lot of exceptions. Performing a backtrace on each one can be slow,
// especially on Windows (where it is very, very slow). So, disable them.
public:
StackTraceMode stackTraceMode() override {
return StackTraceMode::NONE;
}
};
uint64_t traverse(AnyPointer::Reader reader); uint64_t traverse(AnyPointer::Reader reader);
uint64_t traverse(AnyStruct::Reader reader); uint64_t traverse(AnyStruct::Reader reader);
uint64_t traverse(AnyList::Reader reader); uint64_t traverse(AnyList::Reader reader);
...@@ -163,6 +167,7 @@ struct StructChecker { ...@@ -163,6 +167,7 @@ struct StructChecker {
KJ_TEST("fuzz-test struct pointer") { KJ_TEST("fuzz-test struct pointer") {
if (skipFuzzTest()) return; if (skipFuzzTest()) return;
DisableStackTraces disableStackTraces;
MallocMessageBuilder builder; MallocMessageBuilder builder;
builder.getRoot<TestAllTypes>().setTextField("foo"); builder.getRoot<TestAllTypes>().setTextField("foo");
...@@ -183,6 +188,7 @@ struct ListChecker { ...@@ -183,6 +188,7 @@ struct ListChecker {
KJ_TEST("fuzz-test list pointer") { KJ_TEST("fuzz-test list pointer") {
if (skipFuzzTest()) return; if (skipFuzzTest()) return;
DisableStackTraces disableStackTraces;
MallocMessageBuilder builder; MallocMessageBuilder builder;
auto list = builder.getRoot<AnyPointer>().initAs<List<uint32_t>>(2); auto list = builder.getRoot<AnyPointer>().initAs<List<uint32_t>>(2);
...@@ -207,6 +213,7 @@ struct StructListChecker { ...@@ -207,6 +213,7 @@ struct StructListChecker {
KJ_TEST("fuzz-test struct list pointer") { KJ_TEST("fuzz-test struct list pointer") {
if (skipFuzzTest()) return; if (skipFuzzTest()) return;
DisableStackTraces disableStackTraces;
MallocMessageBuilder builder; MallocMessageBuilder builder;
auto list = builder.getRoot<AnyPointer>().initAs<List<test::TestAllTypes>>(2); auto list = builder.getRoot<AnyPointer>().initAs<List<test::TestAllTypes>>(2);
...@@ -230,6 +237,7 @@ struct TextChecker { ...@@ -230,6 +237,7 @@ struct TextChecker {
KJ_TEST("fuzz-test text pointer") { KJ_TEST("fuzz-test text pointer") {
if (skipFuzzTest()) return; if (skipFuzzTest()) return;
DisableStackTraces disableStackTraces;
MallocMessageBuilder builder; MallocMessageBuilder builder;
builder.template getRoot<AnyPointer>().setAs<Text>("foo"); builder.template getRoot<AnyPointer>().setAs<Text>("foo");
...@@ -238,6 +246,7 @@ KJ_TEST("fuzz-test text pointer") { ...@@ -238,6 +246,7 @@ KJ_TEST("fuzz-test text pointer") {
KJ_TEST("fuzz-test far pointer") { KJ_TEST("fuzz-test far pointer") {
if (skipFuzzTest()) return; if (skipFuzzTest()) return;
DisableStackTraces disableStackTraces;
MallocMessageBuilder builder(1, AllocationStrategy::FIXED_SIZE); MallocMessageBuilder builder(1, AllocationStrategy::FIXED_SIZE);
initTestMessage(builder.getRoot<TestAllTypes>()); initTestMessage(builder.getRoot<TestAllTypes>());
...@@ -251,6 +260,7 @@ KJ_TEST("fuzz-test far pointer") { ...@@ -251,6 +260,7 @@ KJ_TEST("fuzz-test far pointer") {
KJ_TEST("fuzz-test double-far pointer") { KJ_TEST("fuzz-test double-far pointer") {
if (skipFuzzTest()) return; if (skipFuzzTest()) return;
DisableStackTraces disableStackTraces;
MallocMessageBuilder builder(1, AllocationStrategy::FIXED_SIZE); MallocMessageBuilder builder(1, AllocationStrategy::FIXED_SIZE);
...@@ -274,5 +284,3 @@ KJ_TEST("fuzz-test double-far pointer") { ...@@ -274,5 +284,3 @@ KJ_TEST("fuzz-test double-far pointer") {
} // namespace } // namespace
} // namespace _ (private) } // namespace _ (private)
} // namespace capnp } // namespace capnp
#endif
...@@ -42,7 +42,7 @@ public: ...@@ -42,7 +42,7 @@ public:
} }
#endif #endif
}; };
static constexpr DummyCapTableReader dummyCapTableReader = DummyCapTableReader(); static KJ_CONSTEXPR(const) DummyCapTableReader dummyCapTableReader = DummyCapTableReader();
} // namespace } // namespace
......
...@@ -242,7 +242,7 @@ protected: ...@@ -242,7 +242,7 @@ protected:
} }
}; };
constexpr MmapDisposer mmapDisposer = MmapDisposer(); KJ_CONSTEXPR(static const) MmapDisposer mmapDisposer = MmapDisposer();
static char* canonicalizePath(char* path) { static char* canonicalizePath(char* path) {
// Taken from some old C code of mine. // Taken from some old C code of mine.
......
...@@ -19,16 +19,12 @@ set(kj_sources_heavy ...@@ -19,16 +19,12 @@ set(kj_sources_heavy
units.c++ units.c++
refcount.c++ refcount.c++
string-tree.c++ string-tree.c++
)
set(kj-parse_sources
parse/char.c++ parse/char.c++
) )
set(kj_sources ${kj_sources_lite})
if(NOT CAPNP_LITE) if(NOT CAPNP_LITE)
list(APPEND kj_sources ${kj_sources_heavy}) set(kj_sources ${kj_sources_lite} ${kj_sources_heavy})
endif() else()
if(CAPNP_BUILD_TOOLS) set(kj_sources ${kj_sources_lite})
list(APPEND kj_sources ${kj-parse_sources})
endif() endif()
set(kj_headers set(kj_headers
...@@ -177,20 +173,12 @@ if(BUILD_TESTING) ...@@ -177,20 +173,12 @@ if(BUILD_TESTING)
one-of-test.c++ one-of-test.c++
function-test.c++ function-test.c++
threadlocal-pthread-test.c++ threadlocal-pthread-test.c++
parse/common-test.c++
parse/char-test.c++
compat/http-test.c++ compat/http-test.c++
) )
target_link_libraries(kj-heavy-tests kj-http kj-async kj-test kj) target_link_libraries(kj-heavy-tests kj-http kj-async kj-test kj)
add_dependencies(check kj-heavy-tests) add_dependencies(check kj-heavy-tests)
add_test(NAME kj-heavy-tests-run COMMAND kj-heavy-tests) add_test(NAME kj-heavy-tests-run COMMAND kj-heavy-tests)
if(CAPNP_BUILD_TOOLS)
add_executable(kj-parse-tests
parse/common-test.c++
parse/char-test.c++
)
target_link_libraries(kj-parse-tests kj-test kj)
add_dependencies(check kj-parse-tests)
add_test(NAME kj-parse-tests-run COMMAND kj-parse-tests)
endif() # CAPNP_BUILD_TOOLS
endif() # NOT CAPNP_LITE endif() # NOT CAPNP_LITE
endif() # BUILD_TESTING endif() # BUILD_TESTING
...@@ -26,9 +26,12 @@ ...@@ -26,9 +26,12 @@
namespace kj { namespace kj {
namespace { namespace {
#if !_MSC_VER
// TODO(msvc): GetFunctorStartAddress is not supported on MSVC currently, so skip the test.
TEST(Async, GetFunctorStartAddress) { TEST(Async, GetFunctorStartAddress) {
EXPECT_TRUE(nullptr != _::GetFunctorStartAddress<>::apply([](){return 0;})); EXPECT_TRUE(nullptr != _::GetFunctorStartAddress<>::apply([](){return 0;}));
} }
#endif
TEST(Async, EvalVoid) { TEST(Async, EvalVoid) {
EventLoop loop; EventLoop loop;
......
...@@ -555,167 +555,179 @@ void testHttpServerRequest(kj::AsyncIoContext& io, ...@@ -555,167 +555,179 @@ void testHttpServerRequest(kj::AsyncIoContext& io,
KJ_EXPECT(service.getRequestCount() == 1); KJ_EXPECT(service.getRequestCount() == 1);
} }
auto HUGE_STRING = kj::strArray(kj::repeat("abcdefgh", 4096), ""); kj::ArrayPtr<const HttpRequestTestCase> requestTestCases() {
auto HUGE_REQUEST = kj::str( static const auto HUGE_STRING = kj::strArray(kj::repeat("abcdefgh", 4096), "");
"GET / HTTP/1.1\r\n" static const auto HUGE_REQUEST = kj::str(
"Host: ", HUGE_STRING, "\r\n" "GET / HTTP/1.1\r\n"
"\r\n"); "Host: ", HUGE_STRING, "\r\n"
"\r\n");
static const HttpRequestTestCase REQUEST_TEST_CASES[] { static const HttpRequestTestCase REQUEST_TEST_CASES[] {
{ {
"GET /foo/bar HTTP/1.1\r\n" "GET /foo/bar HTTP/1.1\r\n"
"Host: example.com\r\n" "Host: example.com\r\n"
"\r\n", "\r\n",
HttpMethod::GET, HttpMethod::GET,
"/foo/bar", "/foo/bar",
{{HttpHeaderId::HOST, "example.com"}}, {{HttpHeaderId::HOST, "example.com"}},
nullptr, {}, nullptr, {},
}, },
{ {
"HEAD /foo/bar HTTP/1.1\r\n" "HEAD /foo/bar HTTP/1.1\r\n"
"Host: example.com\r\n" "Host: example.com\r\n"
"\r\n", "\r\n",
HttpMethod::HEAD, HttpMethod::HEAD,
"/foo/bar", "/foo/bar",
{{HttpHeaderId::HOST, "example.com"}}, {{HttpHeaderId::HOST, "example.com"}},
nullptr, {}, nullptr, {},
}, },
{ {
"POST / HTTP/1.1\r\n" "POST / HTTP/1.1\r\n"
"Content-Length: 9\r\n" "Content-Length: 9\r\n"
"Host: example.com\r\n" "Host: example.com\r\n"
"Content-Type: text/plain\r\n" "Content-Type: text/plain\r\n"
"\r\n" "\r\n"
"foobarbaz", "foobarbaz",
HttpMethod::POST,
"/",
{
{HttpHeaderId::HOST, "example.com"},
{HttpHeaderId::CONTENT_TYPE, "text/plain"},
},
9, { "foo", "bar", "baz" },
},
HttpMethod::POST,
"/",
{ {
{HttpHeaderId::HOST, "example.com"}, "POST / HTTP/1.1\r\n"
{HttpHeaderId::CONTENT_TYPE, "text/plain"}, "Transfer-Encoding: chunked\r\n"
"Host: example.com\r\n"
"Content-Type: text/plain\r\n"
"\r\n"
"3\r\n"
"foo\r\n"
"6\r\n"
"barbaz\r\n"
"0\r\n"
"\r\n",
HttpMethod::POST,
"/",
{
{HttpHeaderId::HOST, "example.com"},
{HttpHeaderId::CONTENT_TYPE, "text/plain"},
},
nullptr, { "foo", "barbaz" },
}, },
9, { "foo", "bar", "baz" },
},
{
"POST / HTTP/1.1\r\n"
"Transfer-Encoding: chunked\r\n"
"Host: example.com\r\n"
"Content-Type: text/plain\r\n"
"\r\n"
"3\r\n"
"foo\r\n"
"6\r\n"
"barbaz\r\n"
"0\r\n"
"\r\n",
HttpMethod::POST,
"/",
{ {
{HttpHeaderId::HOST, "example.com"}, "POST / HTTP/1.1\r\n"
{HttpHeaderId::CONTENT_TYPE, "text/plain"}, "Transfer-Encoding: chunked\r\n"
"Host: example.com\r\n"
"Content-Type: text/plain\r\n"
"\r\n"
"1d\r\n"
"0123456789abcdef0123456789abc\r\n"
"0\r\n"
"\r\n",
HttpMethod::POST,
"/",
{
{HttpHeaderId::HOST, "example.com"},
{HttpHeaderId::CONTENT_TYPE, "text/plain"},
},
nullptr, { "0123456789abcdef0123456789abc" },
}, },
nullptr, { "foo", "barbaz" },
},
{
"POST / HTTP/1.1\r\n"
"Transfer-Encoding: chunked\r\n"
"Host: example.com\r\n"
"Content-Type: text/plain\r\n"
"\r\n"
"1d\r\n"
"0123456789abcdef0123456789abc\r\n"
"0\r\n"
"\r\n",
HttpMethod::POST,
"/",
{ {
{HttpHeaderId::HOST, "example.com"}, HUGE_REQUEST,
{HttpHeaderId::CONTENT_TYPE, "text/plain"},
HttpMethod::GET,
"/",
{{HttpHeaderId::HOST, HUGE_STRING}},
nullptr, {}
}, },
nullptr, { "0123456789abcdef0123456789abc" }, };
},
{ // TODO(cleanup): A bug in GCC 4.8, fixed in 4.9, prevents REQUEST_TEST_CASES from implicitly
HUGE_REQUEST, // casting to our return type.
return kj::arrayPtr(REQUEST_TEST_CASES, kj::size(REQUEST_TEST_CASES));
}
HttpMethod::GET, kj::ArrayPtr<const HttpResponseTestCase> responseTestCases() {
"/", static const HttpResponseTestCase RESPONSE_TEST_CASES[] {
{{HttpHeaderId::HOST, HUGE_STRING}}, {
nullptr, {} "HTTP/1.1 200 OK\r\n"
}, "Content-Type: text/plain\r\n"
}; "Connection: close\r\n"
"\r\n"
"baz qux",
static const HttpResponseTestCase RESPONSE_TEST_CASES[] { 200, "OK",
{ {{HttpHeaderId::CONTENT_TYPE, "text/plain"}},
"HTTP/1.1 200 OK\r\n" nullptr, {"baz qux"},
"Content-Type: text/plain\r\n"
"Connection: close\r\n"
"\r\n"
"baz qux",
200, "OK", HttpMethod::GET,
{{HttpHeaderId::CONTENT_TYPE, "text/plain"}}, CLIENT_ONLY, // Server never sends connection: close
nullptr, {"baz qux"}, },
HttpMethod::GET, {
CLIENT_ONLY, // Server never sends connection: close "HTTP/1.1 200 OK\r\n"
}, "Content-Length: 123\r\n"
"Content-Type: text/plain\r\n"
"\r\n",
{ 200, "OK",
"HTTP/1.1 200 OK\r\n" {{HttpHeaderId::CONTENT_TYPE, "text/plain"}},
"Content-Length: 123\r\n" 123, {},
"Content-Type: text/plain\r\n"
"\r\n",
200, "OK", HttpMethod::HEAD,
{{HttpHeaderId::CONTENT_TYPE, "text/plain"}}, },
123, {},
HttpMethod::HEAD, {
}, "HTTP/1.1 200 OK\r\n"
"Content-Length: 8\r\n"
"Content-Type: text/plain\r\n"
"\r\n"
"quxcorge",
{ 200, "OK",
"HTTP/1.1 200 OK\r\n" {{HttpHeaderId::CONTENT_TYPE, "text/plain"}},
"Content-Length: 8\r\n" 8, { "qux", "corge" }
"Content-Type: text/plain\r\n" },
"\r\n"
"quxcorge",
200, "OK", {
{{HttpHeaderId::CONTENT_TYPE, "text/plain"}}, "HTTP/1.1 200 OK\r\n"
8, { "qux", "corge" } "Transfer-Encoding: chunked\r\n"
}, "Content-Type: text/plain\r\n"
"\r\n"
"3\r\n"
"qux\r\n"
"5\r\n"
"corge\r\n"
"0\r\n"
"\r\n",
{ 200, "OK",
"HTTP/1.1 200 OK\r\n" {{HttpHeaderId::CONTENT_TYPE, "text/plain"}},
"Transfer-Encoding: chunked\r\n" nullptr, { "qux", "corge" }
"Content-Type: text/plain\r\n" },
"\r\n" };
"3\r\n"
"qux\r\n"
"5\r\n"
"corge\r\n"
"0\r\n"
"\r\n",
200, "OK", // TODO(cleanup): A bug in GCC 4.8, fixed in 4.9, prevents RESPONSE_TEST_CASES from implicitly
{{HttpHeaderId::CONTENT_TYPE, "text/plain"}}, // casting to our return type.
nullptr, { "qux", "corge" } return kj::arrayPtr(RESPONSE_TEST_CASES, kj::size(RESPONSE_TEST_CASES));
}, }
};
KJ_TEST("HttpClient requests") { KJ_TEST("HttpClient requests") {
auto io = kj::setupAsyncIo(); auto io = kj::setupAsyncIo();
for (auto& testCase: REQUEST_TEST_CASES) { for (auto& testCase: requestTestCases()) {
if (testCase.side == SERVER_ONLY) continue; if (testCase.side == SERVER_ONLY) continue;
KJ_CONTEXT(testCase.raw); KJ_CONTEXT(testCase.raw);
testHttpClientRequest(io, testCase); testHttpClientRequest(io, testCase);
...@@ -726,7 +738,7 @@ KJ_TEST("HttpClient responses") { ...@@ -726,7 +738,7 @@ KJ_TEST("HttpClient responses") {
auto io = kj::setupAsyncIo(); auto io = kj::setupAsyncIo();
size_t FRAGMENT_SIZES[] = { 1, 2, 3, 4, 5, 6, 7, 8, 16, 31, kj::maxValue }; size_t FRAGMENT_SIZES[] = { 1, 2, 3, 4, 5, 6, 7, 8, 16, 31, kj::maxValue };
for (auto& testCase: RESPONSE_TEST_CASES) { for (auto& testCase: responseTestCases()) {
if (testCase.side == SERVER_ONLY) continue; if (testCase.side == SERVER_ONLY) continue;
for (size_t fragmentSize: FRAGMENT_SIZES) { for (size_t fragmentSize: FRAGMENT_SIZES) {
KJ_CONTEXT(testCase.raw, fragmentSize); KJ_CONTEXT(testCase.raw, fragmentSize);
...@@ -759,7 +771,7 @@ KJ_TEST("HttpServer requests") { ...@@ -759,7 +771,7 @@ KJ_TEST("HttpServer requests") {
auto io = kj::setupAsyncIo(); auto io = kj::setupAsyncIo();
for (auto& testCase: REQUEST_TEST_CASES) { for (auto& testCase: requestTestCases()) {
if (testCase.side == CLIENT_ONLY) continue; if (testCase.side == CLIENT_ONLY) continue;
KJ_CONTEXT(testCase.raw); KJ_CONTEXT(testCase.raw);
testHttpServerRequest(io, testCase, testHttpServerRequest(io, testCase,
...@@ -790,7 +802,7 @@ KJ_TEST("HttpServer responses") { ...@@ -790,7 +802,7 @@ KJ_TEST("HttpServer responses") {
auto io = kj::setupAsyncIo(); auto io = kj::setupAsyncIo();
for (auto& testCase: RESPONSE_TEST_CASES) { for (auto& testCase: responseTestCases()) {
if (testCase.side == CLIENT_ONLY) continue; if (testCase.side == CLIENT_ONLY) continue;
KJ_CONTEXT(testCase.raw); KJ_CONTEXT(testCase.raw);
testHttpServerRequest(io, testHttpServerRequest(io,
...@@ -800,90 +812,98 @@ KJ_TEST("HttpServer responses") { ...@@ -800,90 +812,98 @@ KJ_TEST("HttpServer responses") {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
static const HttpTestCase PIPELINE_TESTS[] = { kj::ArrayPtr<const HttpTestCase> pipelineTestCases() {
{ static const HttpTestCase PIPELINE_TESTS[] = {
{ {
"GET / HTTP/1.1\r\n" {
"\r\n", "GET / HTTP/1.1\r\n"
"\r\n",
HttpMethod::GET, "/", {}, nullptr, {}, HttpMethod::GET, "/", {}, nullptr, {},
}, },
{ {
"HTTP/1.1 200 OK\r\n" "HTTP/1.1 200 OK\r\n"
"Content-Length: 7\r\n" "Content-Length: 7\r\n"
"\r\n" "\r\n"
"foo bar", "foo bar",
200, "OK", {}, 7, { "foo bar" } 200, "OK", {}, 7, { "foo bar" }
},
}, },
},
{
{ {
"POST /foo HTTP/1.1\r\n" {
"Content-Length: 6\r\n" "POST /foo HTTP/1.1\r\n"
"\r\n" "Content-Length: 6\r\n"
"grault", "\r\n"
"grault",
HttpMethod::POST, "/foo", {}, 6, { "grault" },
HttpMethod::POST, "/foo", {}, 6, { "grault" },
},
{
"HTTP/1.1 404 Not Found\r\n"
"Content-Length: 13\r\n"
"\r\n"
"baz qux corge",
404, "Not Found", {}, 13, { "baz qux corge" }
},
}, },
{
"HTTP/1.1 404 Not Found\r\n"
"Content-Length: 13\r\n"
"\r\n"
"baz qux corge",
404, "Not Found", {}, 13, { "baz qux corge" }
},
},
{
{
"POST /bar HTTP/1.1\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"6\r\n"
"garply\r\n"
"5\r\n"
"waldo\r\n"
"0\r\n"
"\r\n",
HttpMethod::POST, "/bar", {}, nullptr, { "garply", "waldo" },
},
{ {
"HTTP/1.1 200 OK\r\n" {
"Transfer-Encoding: chunked\r\n" "POST /bar HTTP/1.1\r\n"
"\r\n" "Transfer-Encoding: chunked\r\n"
"4\r\n" "\r\n"
"fred\r\n" "6\r\n"
"5\r\n" "garply\r\n"
"plugh\r\n" "5\r\n"
"0\r\n" "waldo\r\n"
"\r\n", "0\r\n"
"\r\n",
200, "OK", {}, nullptr, { "fred", "plugh" }
HttpMethod::POST, "/bar", {}, nullptr, { "garply", "waldo" },
},
{
"HTTP/1.1 200 OK\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"4\r\n"
"fred\r\n"
"5\r\n"
"plugh\r\n"
"0\r\n"
"\r\n",
200, "OK", {}, nullptr, { "fred", "plugh" }
},
}, },
},
{
{ {
"HEAD / HTTP/1.1\r\n" {
"\r\n", "HEAD / HTTP/1.1\r\n"
"\r\n",
HttpMethod::HEAD, "/", {}, nullptr, {}, HttpMethod::HEAD, "/", {}, nullptr, {},
}, },
{ {
"HTTP/1.1 200 OK\r\n" "HTTP/1.1 200 OK\r\n"
"Content-Length: 7\r\n" "Content-Length: 7\r\n"
"\r\n", "\r\n",
200, "OK", {}, 7, { "foo bar" } 200, "OK", {}, 7, { "foo bar" }
},
}, },
}, };
};
// TODO(cleanup): A bug in GCC 4.8, fixed in 4.9, prevents RESPONSE_TEST_CASES from implicitly
// casting to our return type.
return kj::arrayPtr(PIPELINE_TESTS, kj::size(PIPELINE_TESTS));
}
KJ_TEST("HttpClient pipeline") { KJ_TEST("HttpClient pipeline") {
auto PIPELINE_TESTS = pipelineTestCases();
auto io = kj::setupAsyncIo(); auto io = kj::setupAsyncIo();
auto pipe = io.provider->newTwoWayPipe(); auto pipe = io.provider->newTwoWayPipe();
...@@ -933,6 +953,8 @@ KJ_TEST("HttpClient pipeline") { ...@@ -933,6 +953,8 @@ KJ_TEST("HttpClient pipeline") {
} }
KJ_TEST("HttpClient parallel pipeline") { KJ_TEST("HttpClient parallel pipeline") {
auto PIPELINE_TESTS = pipelineTestCases();
auto io = kj::setupAsyncIo(); auto io = kj::setupAsyncIo();
auto pipe = io.provider->newTwoWayPipe(); auto pipe = io.provider->newTwoWayPipe();
...@@ -986,6 +1008,8 @@ KJ_TEST("HttpClient parallel pipeline") { ...@@ -986,6 +1008,8 @@ KJ_TEST("HttpClient parallel pipeline") {
} }
KJ_TEST("HttpServer pipeline") { KJ_TEST("HttpServer pipeline") {
auto PIPELINE_TESTS = pipelineTestCases();
auto io = kj::setupAsyncIo(); auto io = kj::setupAsyncIo();
auto pipe = io.provider->newTwoWayPipe(); auto pipe = io.provider->newTwoWayPipe();
...@@ -1011,6 +1035,8 @@ KJ_TEST("HttpServer pipeline") { ...@@ -1011,6 +1035,8 @@ KJ_TEST("HttpServer pipeline") {
} }
KJ_TEST("HttpServer parallel pipeline") { KJ_TEST("HttpServer parallel pipeline") {
auto PIPELINE_TESTS = pipelineTestCases();
auto io = kj::setupAsyncIo(); auto io = kj::setupAsyncIo();
auto pipe = io.provider->newTwoWayPipe(); auto pipe = io.provider->newTwoWayPipe();
...@@ -1037,6 +1063,8 @@ KJ_TEST("HttpServer parallel pipeline") { ...@@ -1037,6 +1063,8 @@ KJ_TEST("HttpServer parallel pipeline") {
} }
KJ_TEST("HttpClient <-> HttpServer") { KJ_TEST("HttpClient <-> HttpServer") {
auto PIPELINE_TESTS = pipelineTestCases();
auto io = kj::setupAsyncIo(); auto io = kj::setupAsyncIo();
auto pipe = io.provider->newTwoWayPipe(); auto pipe = io.provider->newTwoWayPipe();
...@@ -1082,6 +1110,8 @@ KJ_TEST("HttpClient <-> HttpServer") { ...@@ -1082,6 +1110,8 @@ KJ_TEST("HttpClient <-> HttpServer") {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
KJ_TEST("HttpServer request timeout") { KJ_TEST("HttpServer request timeout") {
auto PIPELINE_TESTS = pipelineTestCases();
auto io = kj::setupAsyncIo(); auto io = kj::setupAsyncIo();
auto pipe = io.provider->newTwoWayPipe(); auto pipe = io.provider->newTwoWayPipe();
...@@ -1100,6 +1130,8 @@ KJ_TEST("HttpServer request timeout") { ...@@ -1100,6 +1130,8 @@ KJ_TEST("HttpServer request timeout") {
} }
KJ_TEST("HttpServer pipeline timeout") { KJ_TEST("HttpServer pipeline timeout") {
auto PIPELINE_TESTS = pipelineTestCases();
auto io = kj::setupAsyncIo(); auto io = kj::setupAsyncIo();
auto pipe = io.provider->newTwoWayPipe(); auto pipe = io.provider->newTwoWayPipe();
...@@ -1146,6 +1178,8 @@ private: ...@@ -1146,6 +1178,8 @@ private:
}; };
KJ_TEST("HttpServer no response") { KJ_TEST("HttpServer no response") {
auto PIPELINE_TESTS = pipelineTestCases();
auto io = kj::setupAsyncIo(); auto io = kj::setupAsyncIo();
auto pipe = io.provider->newTwoWayPipe(); auto pipe = io.provider->newTwoWayPipe();
...@@ -1170,6 +1204,8 @@ KJ_TEST("HttpServer no response") { ...@@ -1170,6 +1204,8 @@ KJ_TEST("HttpServer no response") {
} }
KJ_TEST("HttpServer disconnected") { KJ_TEST("HttpServer disconnected") {
auto PIPELINE_TESTS = pipelineTestCases();
auto io = kj::setupAsyncIo(); auto io = kj::setupAsyncIo();
auto pipe = io.provider->newTwoWayPipe(); auto pipe = io.provider->newTwoWayPipe();
...@@ -1188,6 +1224,8 @@ KJ_TEST("HttpServer disconnected") { ...@@ -1188,6 +1224,8 @@ KJ_TEST("HttpServer disconnected") {
} }
KJ_TEST("HttpServer overloaded") { KJ_TEST("HttpServer overloaded") {
auto PIPELINE_TESTS = pipelineTestCases();
auto io = kj::setupAsyncIo(); auto io = kj::setupAsyncIo();
auto pipe = io.provider->newTwoWayPipe(); auto pipe = io.provider->newTwoWayPipe();
...@@ -1206,6 +1244,8 @@ KJ_TEST("HttpServer overloaded") { ...@@ -1206,6 +1244,8 @@ KJ_TEST("HttpServer overloaded") {
} }
KJ_TEST("HttpServer unimplemented") { KJ_TEST("HttpServer unimplemented") {
auto PIPELINE_TESTS = pipelineTestCases();
auto io = kj::setupAsyncIo(); auto io = kj::setupAsyncIo();
auto pipe = io.provider->newTwoWayPipe(); auto pipe = io.provider->newTwoWayPipe();
...@@ -1224,6 +1264,8 @@ KJ_TEST("HttpServer unimplemented") { ...@@ -1224,6 +1264,8 @@ KJ_TEST("HttpServer unimplemented") {
} }
KJ_TEST("HttpServer threw exception") { KJ_TEST("HttpServer threw exception") {
auto PIPELINE_TESTS = pipelineTestCases();
auto io = kj::setupAsyncIo(); auto io = kj::setupAsyncIo();
auto pipe = io.provider->newTwoWayPipe(); auto pipe = io.provider->newTwoWayPipe();
...@@ -1264,6 +1306,8 @@ private: ...@@ -1264,6 +1306,8 @@ private:
}; };
KJ_TEST("HttpServer threw exception after starting response") { KJ_TEST("HttpServer threw exception after starting response") {
auto PIPELINE_TESTS = pipelineTestCases();
auto io = kj::setupAsyncIo(); auto io = kj::setupAsyncIo();
auto pipe = io.provider->newTwoWayPipe(); auto pipe = io.provider->newTwoWayPipe();
......
...@@ -150,6 +150,7 @@ Exception::Type typeOfWin32Error(DWORD error) { ...@@ -150,6 +150,7 @@ Exception::Type typeOfWin32Error(DWORD error) {
case WSAENETDOWN: case WSAENETDOWN:
case WSAENETRESET: case WSAENETRESET:
case WSAENETUNREACH: case WSAENETUNREACH:
case WSAESHUTDOWN:
return Exception::Type::DISCONNECTED; return Exception::Type::DISCONNECTED;
case WSAEOPNOTSUPP: case WSAEOPNOTSUPP:
......
...@@ -28,6 +28,9 @@ namespace _ { // private ...@@ -28,6 +28,9 @@ namespace _ { // private
namespace { namespace {
TEST(Exception, TrimSourceFilename) { TEST(Exception, TrimSourceFilename) {
#if _WIN32
if (trimSourceFilename(__FILE__) != "kj\\exception-test.c++")
#endif
EXPECT_EQ(trimSourceFilename(__FILE__), "kj/exception-test.c++"); EXPECT_EQ(trimSourceFilename(__FILE__), "kj/exception-test.c++");
} }
......
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
#include <dbghelp.h> #include <dbghelp.h>
#endif #endif
#if (__linux__ || __APPLE__) && defined(KJ_DEBUG) #if (__linux__ || __APPLE__)
#include <stdio.h> #include <stdio.h>
#include <pthread.h> #include <pthread.h>
#endif #endif
...@@ -156,6 +156,10 @@ ArrayPtr<void* const> getStackTrace(ArrayPtr<void*> space, uint ignoreCount, ...@@ -156,6 +156,10 @@ ArrayPtr<void* const> getStackTrace(ArrayPtr<void*> space, uint ignoreCount,
#endif #endif
ArrayPtr<void* const> getStackTrace(ArrayPtr<void*> space, uint ignoreCount) { ArrayPtr<void* const> getStackTrace(ArrayPtr<void*> space, uint ignoreCount) {
if (getExceptionCallback().stackTraceMode() == ExceptionCallback::StackTraceMode::NONE) {
return nullptr;
}
#if _WIN32 && _M_X64 #if _WIN32 && _M_X64
CONTEXT context; CONTEXT context;
RtlCaptureContext(&context); RtlCaptureContext(&context);
...@@ -170,11 +174,11 @@ ArrayPtr<void* const> getStackTrace(ArrayPtr<void*> space, uint ignoreCount) { ...@@ -170,11 +174,11 @@ ArrayPtr<void* const> getStackTrace(ArrayPtr<void*> space, uint ignoreCount) {
String stringifyStackTrace(ArrayPtr<void* const> trace) { String stringifyStackTrace(ArrayPtr<void* const> trace) {
if (trace.size() == 0) return nullptr; if (trace.size() == 0) return nullptr;
if (getExceptionCallback().stackTraceMode() != ExceptionCallback::StackTraceMode::FULL) {
return nullptr;
}
#ifndef KJ_DEBUG #if _WIN32 && _M_X64 && _MSC_VER
return nullptr;
#elif _WIN32 && _M_X64 && _MSC_VER
// Try to get file/line using SymGetLineFromAddr64(). We don't bother if we aren't on MSVC since // Try to get file/line using SymGetLineFromAddr64(). We don't bother if we aren't on MSVC since
// this requires MSVC debug info. // this requires MSVC debug info.
...@@ -407,17 +411,29 @@ kj::StringPtr trimSourceFilename(kj::StringPtr filename) { ...@@ -407,17 +411,29 @@ kj::StringPtr trimSourceFilename(kj::StringPtr filename) {
// To deal with all this, we look for directory names in the path which we recognize to be // To deal with all this, we look for directory names in the path which we recognize to be
// locations that represent roots of the source tree. We strip said root and everything before // locations that represent roots of the source tree. We strip said root and everything before
// it. // it.
//
// On Windows, we often get filenames containing backslashes. Since we aren't allowed to allocate
// a new string here, we can't do much about this, so our returned "canonical" name will
// unfortunately end up with backslashes.
static constexpr const char* ROOTS[] = { static constexpr const char* ROOTS[] = {
"ekam-provider/canonical/", // Ekam source file. "ekam-provider/canonical/", // Ekam source file.
"ekam-provider/c++header/", // Ekam include file. "ekam-provider/c++header/", // Ekam include file.
"src/", // Non-Ekam source root. "src/", // Non-Ekam source root.
"tmp/" // Non-Ekam generated code. "tmp/", // Non-Ekam generated code.
#if _WIN32
"src\\", // Win32 source root.
"tmp\\", // Win32 generated code.
#endif
}; };
retry: retry:
for (size_t i: kj::indices(filename)) { for (size_t i: kj::indices(filename)) {
if (i == 0 || filename[i-1] == '/') { if (i == 0 || filename[i-1] == '/'
#if _WIN32
|| filename[i-1] == '\\'
#endif
) {
// We're at the start of a directory name. Check for valid prefixes. // We're at the start of a directory name. Check for valid prefixes.
for (kj::StringPtr root: ROOTS) { for (kj::StringPtr root: ROOTS) {
if (filename.slice(i).startsWith(root)) { if (filename.slice(i).startsWith(root)) {
...@@ -629,6 +645,10 @@ void ExceptionCallback::logMessage( ...@@ -629,6 +645,10 @@ void ExceptionCallback::logMessage(
next.logMessage(severity, file, line, contextDepth, mv(text)); next.logMessage(severity, file, line, contextDepth, mv(text));
} }
ExceptionCallback::StackTraceMode ExceptionCallback::stackTraceMode() {
return next.stackTraceMode();
}
class ExceptionCallback::RootExceptionCallback: public ExceptionCallback { class ExceptionCallback::RootExceptionCallback: public ExceptionCallback {
public: public:
RootExceptionCallback(): ExceptionCallback(*this) {} RootExceptionCallback(): ExceptionCallback(*this) {}
...@@ -675,6 +695,14 @@ public: ...@@ -675,6 +695,14 @@ public:
} }
} }
StackTraceMode stackTraceMode() override {
#ifdef KJ_DEBUG
return StackTraceMode::FULL;
#else
return StackTraceMode::ADDRESS_ONLY;
#endif
}
private: private:
void logException(LogSeverity severity, Exception&& e) { void logException(LogSeverity severity, Exception&& e) {
// We intentionally go back to the top exception callback on the stack because we don't want to // We intentionally go back to the top exception callback on the stack because we don't want to
......
...@@ -193,6 +193,29 @@ public: ...@@ -193,6 +193,29 @@ public:
// //
// The global default implementation writes the text to stderr. // The global default implementation writes the text to stderr.
enum class StackTraceMode {
FULL,
// Stringifying a stack trace will attempt to determine source file and line numbers. This may
// be expensive. For example, on Linux, this shells out to `addr2line`.
//
// This is the default in debug builds.
ADDRESS_ONLY,
// Stringifying a stack trace will only generate a list of code addresses.
//
// This is the default in release builds.
NONE
// Generating a stack trace will always return an empty array.
//
// This avoids ever unwinding the stack. On Windows in particular, the stack unwinding library
// has been observed to be pretty slow, so exception-heavy code might benefit significantly
// from this setting. (But exceptions should be rare...)
};
virtual StackTraceMode stackTraceMode();
// Returns the current preferred stack trace mode.
protected: protected:
ExceptionCallback& next; ExceptionCallback& next;
......
...@@ -45,6 +45,9 @@ ...@@ -45,6 +45,9 @@
#include "../array.h" #include "../array.h"
#include "../tuple.h" #include "../tuple.h"
#include "../vector.h" #include "../vector.h"
#if _MSC_VER
#include <type_traits> // result_of_t
#endif
namespace kj { namespace kj {
namespace parse { namespace parse {
...@@ -100,7 +103,15 @@ private: ...@@ -100,7 +103,15 @@ private:
template <typename T> struct OutputType_; template <typename T> struct OutputType_;
template <typename T> struct OutputType_<Maybe<T>> { typedef T Type; }; template <typename T> struct OutputType_<Maybe<T>> { typedef T Type; };
template <typename Parser, typename Input> template <typename Parser, typename Input>
using OutputType = typename OutputType_<decltype(instance<Parser&>()(instance<Input&>()))>::Type; using OutputType = typename OutputType_<
#if _MSC_VER
std::result_of_t<Parser(Input)>
// The instance<T&>() based version below results in:
// C2064: term does not evaluate to a function taking 1 arguments
#else
decltype(instance<Parser&>()(instance<Input&>()))
#endif
>::Type;
// Synonym for the output type of a parser, given the parser type and the input type. // Synonym for the output type of a parser, given the parser type and the input type.
// ======================================================================================= // =======================================================================================
...@@ -122,12 +133,12 @@ public: ...@@ -122,12 +133,12 @@ public:
template <typename Other> template <typename Other>
constexpr ParserRef(Other&& other) constexpr ParserRef(Other&& other)
: parser(&other), wrapper(&WrapperImplInstance<Decay<Other>>::instance) { : parser(&other), wrapper(&WrapperImplInstance<Decay<Other>>::instance) {
static_assert(kj::isReference<Other>(), "ParseRef should not be assigned to a temporary."); static_assert(kj::isReference<Other>(), "ParserRef should not be assigned to a temporary.");
} }
template <typename Other> template <typename Other>
inline ParserRef& operator=(Other&& other) { inline ParserRef& operator=(Other&& other) {
static_assert(kj::isReference<Other>(), "ParseRef should not be assigned to a temporary."); static_assert(kj::isReference<Other>(), "ParserRef should not be assigned to a temporary.");
parser = &other; parser = &other;
wrapper = &WrapperImplInstance<Decay<Other>>::instance; wrapper = &WrapperImplInstance<Decay<Other>>::instance;
return *this; return *this;
...@@ -151,7 +162,13 @@ private: ...@@ -151,7 +162,13 @@ private:
}; };
template <typename ParserImpl> template <typename ParserImpl>
struct WrapperImplInstance { struct WrapperImplInstance {
#if _MSC_VER
// TODO(msvc): MSVC currently fails to initialize vtable pointers for constexpr values so
// we have to make this just const instead.
static const WrapperImpl<ParserImpl> instance;
#else
static constexpr WrapperImpl<ParserImpl> instance = WrapperImpl<ParserImpl>(); static constexpr WrapperImpl<ParserImpl> instance = WrapperImpl<ParserImpl>();
#endif
}; };
const void* parser; const void* parser;
...@@ -160,8 +177,13 @@ private: ...@@ -160,8 +177,13 @@ private:
template <typename Input, typename Output> template <typename Input, typename Output>
template <typename ParserImpl> template <typename ParserImpl>
#if _MSC_VER
const typename ParserRef<Input, Output>::template WrapperImpl<ParserImpl>
ParserRef<Input, Output>::WrapperImplInstance<ParserImpl>::instance = WrapperImpl<ParserImpl>();
#else
constexpr typename ParserRef<Input, Output>::template WrapperImpl<ParserImpl> constexpr typename ParserRef<Input, Output>::template WrapperImpl<ParserImpl>
ParserRef<Input, Output>::WrapperImplInstance<ParserImpl>::instance; ParserRef<Input, Output>::WrapperImplInstance<ParserImpl>::instance;
#endif
template <typename Input, typename ParserImpl> template <typename Input, typename ParserImpl>
constexpr ParserRef<Input, OutputType<ParserImpl, Input>> ref(ParserImpl& impl) { constexpr ParserRef<Input, OutputType<ParserImpl, Input>> ref(ParserImpl& impl) {
...@@ -298,29 +320,46 @@ public: ...@@ -298,29 +320,46 @@ public:
explicit constexpr Sequence_(T&& firstSubParser, U&&... rest) explicit constexpr Sequence_(T&& firstSubParser, U&&... rest)
: first(kj::fwd<T>(firstSubParser)), rest(kj::fwd<U>(rest)...) {} : first(kj::fwd<T>(firstSubParser)), rest(kj::fwd<U>(rest)...) {}
// TODO(msvc): MSVC ICEs on the return types of operator() and parseNext() unless SubParsers is // TODO(msvc): The trailing return types on `operator()` and `parseNext()` expose at least two
// wrapped in `decltype(instance<SubParsers>())`. This is similar to the workaround in // bugs in MSVC:
// KJ_CONTEXT(). //
// 1. An ICE.
// 2. 'error C2672: 'operator __surrogate_func': no matching overloaded function found)',
// which crops up in numerous places when trying to build the capnp command line tools.
//
// The only workaround I found for both bugs is to omit the trailing return types and instead
// rely on C++14's return type deduction.
template <typename Input> template <typename Input>
auto operator()(Input& input) const -> auto operator()(Input& input) const
Maybe<decltype(tuple( #ifndef _MSC_VER
-> Maybe<decltype(tuple(
instance<OutputType<FirstSubParser, Input>>(), instance<OutputType<FirstSubParser, Input>>(),
instance<OutputType<decltype(instance<SubParsers>()), Input>>()...))> { instance<OutputType<SubParsers, Input>>()...))>
#endif
{
return parseNext(input); return parseNext(input);
} }
template <typename Input, typename... InitialParams> template <typename Input, typename... InitialParams>
auto parseNext(Input& input, InitialParams&&... initialParams) const -> auto parseNext(Input& input, InitialParams&&... initialParams) const
Maybe<decltype(tuple( #ifndef _MSC_VER
-> Maybe<decltype(tuple(
kj::fwd<InitialParams>(initialParams)..., kj::fwd<InitialParams>(initialParams)...,
instance<OutputType<FirstSubParser, Input>>(), instance<OutputType<FirstSubParser, Input>>(),
instance<OutputType<decltype(instance<SubParsers>()), Input>>()...))> { instance<OutputType<SubParsers, Input>>()...))>
#endif
{
KJ_IF_MAYBE(firstResult, first(input)) { KJ_IF_MAYBE(firstResult, first(input)) {
return rest.parseNext(input, kj::fwd<InitialParams>(initialParams)..., return rest.parseNext(input, kj::fwd<InitialParams>(initialParams)...,
kj::mv(*firstResult)); kj::mv(*firstResult));
} else { } else {
return nullptr; // TODO(msvc): MSVC depends on return type deduction to compile this function, so we need to
// help it deduce the right type on this code path.
return Maybe<decltype(tuple(
kj::fwd<InitialParams>(initialParams)...,
instance<OutputType<FirstSubParser, Input>>(),
instance<OutputType<SubParsers, Input>>()...))>{nullptr};
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment