Skip to content

Commit aa32df6

Browse files
authored
test: integration-tests for transport compression (#969)
1 parent 0a11dc0 commit aa32df6

File tree

11 files changed

+201
-74
lines changed

11 files changed

+201
-74
lines changed

.github/workflows/ci.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,11 @@ jobs:
158158
MINGW_ASM_MASM_COMPILER: ${{ matrix.MINGW_ASM_MASM_COMPILER }}
159159
run: . "scripts\install-llvm-mingw.ps1"
160160

161+
- name: Set up zlib for Windows
162+
if: ${{ runner.os == 'Windows' }}
163+
shell: powershell
164+
run: . "scripts\install-zlib.ps1"
165+
161166
- name: Installing Android SDK Dependencies
162167
if: ${{ env['ANDROID_API'] }}
163168
run: |

scripts/install-llvm-mingw.ps1

Lines changed: 8 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ New-Item -ItemType Directory -Force -Path "${LLVM_MINGW_INSTALL_PATH}"
2626
Expand-Archive -LiteralPath "${LLVM_MINGW_DL_PATH}" -DestinationPath "${LLVM_MINGW_INSTALL_PATH}"
2727
# Export the LLVM-mingw install path
2828
$LLVM_MINGW_INSTALL_PATH = "${LLVM_MINGW_INSTALL_PATH}\${LLVM_MINGW_PKG}"
29-
Write-Output "LLVM_MINGW_INSTALL_PATH=${LLVM_MINGW_INSTALL_PATH}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
29+
"LLVM_MINGW_INSTALL_PATH=${LLVM_MINGW_INSTALL_PATH}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
3030
# Prepend bin path to the system PATH
31-
Write-Output "Path to LLVM-mingw bin folder: ${LLVM_MINGW_INSTALL_PATH}\bin"
32-
Write-Output "${LLVM_MINGW_INSTALL_PATH}\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
31+
Write-Host "Path to LLVM-mingw bin folder: ${LLVM_MINGW_INSTALL_PATH}\bin"
32+
"${LLVM_MINGW_INSTALL_PATH}\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
3333

3434
# Download ninja-build
3535
$NINJA_DL_URL = "https://github.com/ninja-build/ninja/releases/download/v1.11.1/ninja-win.zip"
@@ -49,36 +49,10 @@ Write-Host "Extracting Ninja-Build..."
4949
$NINJA_INSTALL_PATH = "$env:GITHUB_WORKSPACE\buildtools\ninja"
5050
New-Item -ItemType Directory -Force -Path "${NINJA_INSTALL_PATH}"
5151
Expand-Archive -LiteralPath "${NINJA_DL_PATH}" -DestinationPath "${NINJA_INSTALL_PATH}"
52-
# Export the NINJA executable path
53-
Write-Output "NINJA_INSTALL_PATH=${NINJA_INSTALL_PATH}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
54-
Write-Output "PATH=${NINJA_INSTALL_PATH}; $env:PATH" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
55-
$env:PATH = "${NINJA_INSTALL_PATH}; $env:PATH"
56-
57-
# Download zlib
58-
$ZLIB_RELEASE = "1.3.1";
59-
$ZLIB_RELEASE_ASSET = "zlib131.zip"
60-
$ZLIB_DL_URL = "https://github.com/madler/zlib/releases/download/v${ZLIB_RELEASE}/${ZLIB_RELEASE_ASSET}"
61-
$ZLIB_DL_SHA512 = "1f171880153b0120e1364baaf7d0a17f65086eff279f8f8c8538e5950097d1feee37cc173181676ba1e2aeb4565ba68749c814cd3e25bfb06271bea02feb7d94"
62-
$ZLIB_DL_PATH = "${DL_BASEDIR}\${ZLIB_RELEASE_ASSET}"
63-
$CurlArguments = '-s', '-Lf', '-o', "${ZLIB_DL_PATH}", "${ZLIB_DL_URL}"
64-
& curl.exe @CurlArguments
65-
$zlib_zip_hash = Get-FileHash -LiteralPath "${ZLIB_DL_PATH}" -Algorithm SHA512
66-
if ($zlib_zip_hash.Hash -eq $ZLIB_DL_SHA512) {
67-
Write-Host "Successfully downloaded ${ZLIB_RELEASE_ASSET}"
68-
}
69-
Else {
70-
Write-Error "The downloaded ${ZLIB_RELEASE_ASSET} hash '$($zlib_zip_hash.Hash)' does not match the expected hash: '$ZLIB_DL_SHA512'"
71-
}
7252

73-
Write-Host "Extracting zlib source..."
74-
$ZLIB_SOURCE_PATH = "$env:GITHUB_WORKSPACE\buildtools\zlib-${ZLIB_RELEASE}"
75-
Expand-Archive -LiteralPath "${ZLIB_DL_PATH}" -DestinationPath "$env:GITHUB_WORKSPACE\buildtools"
76-
77-
Write-Host "Building zlib source..."
78-
$ZLIB_BUILD_PATH = "$env:GITHUB_WORKSPACE\buildtools\zlib_build"
79-
cmake.exe -B "${ZLIB_BUILD_PATH}" -S "${ZLIB_SOURCE_PATH}" -DCMAKE_C_COMPILER="${env:MINGW_PKG_PREFIX}-gcc" -DCMAKE_CXX_COMPILER="${env:MINGW_PKG_PREFIX}-g++" -DCMAKE_RC_COMPILER="${env:MINGW_PKG_PREFIX}-windres" -DCMAKE_ASM_MASM_COMPILER="${env:MINGW_ASM_MASM_COMPILER}" -GNinja
80-
cmake.exe --build "${ZLIB_BUILD_PATH}" --target zlibstatic
81-
Copy-Item "${ZLIB_SOURCE_PATH}\zlib.h" "${ZLIB_BUILD_PATH}"
53+
# Export the NINJA executable path
54+
"NINJA_INSTALL_PATH=${NINJA_INSTALL_PATH}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
55+
"PATH=${NINJA_INSTALL_PATH}; $env:PATH" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
8256

83-
# Add CMAKE_DEFINES
84-
Write-Output "CMAKE_DEFINES=-DZLIB_LIBRARY=${ZLIB_BUILD_PATH}\libzlibstatic.a -DZLIB_INCLUDE_DIR=${ZLIB_BUILD_PATH} -DCMAKE_C_COMPILER=${env:MINGW_PKG_PREFIX}-gcc -DCMAKE_CXX_COMPILER=${env:MINGW_PKG_PREFIX}-g++ -DCMAKE_RC_COMPILER=${env:MINGW_PKG_PREFIX}-windres -DCMAKE_ASM_MASM_COMPILER=${env:MINGW_ASM_MASM_COMPILER} -GNinja" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
57+
# export CMAKE_DEFINES to the runner environment
58+
"CMAKE_DEFINES=-DCMAKE_C_COMPILER=${env:MINGW_PKG_PREFIX}-gcc -DCMAKE_CXX_COMPILER=${env:MINGW_PKG_PREFIX}-g++ -DCMAKE_RC_COMPILER=${env:MINGW_PKG_PREFIX}-windres -DCMAKE_ASM_MASM_COMPILER=${env:MINGW_ASM_MASM_COMPILER}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append

scripts/install-zlib.ps1

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
$DL_BASEDIR = "$env:GITHUB_WORKSPACE\dl"
2+
if (!(Test-Path -Path "$DL_BASEDIR")) { New-Item -ItemType Directory -Force -Path "$DL_BASEDIR" }
3+
4+
$ZLIB_RELEASE = "1.3.1";
5+
$ZLIB_RELEASE_ASSET = "zlib131.zip"
6+
$ZLIB_DL_URL = "https://github.com/madler/zlib/releases/download/v${ZLIB_RELEASE}/${ZLIB_RELEASE_ASSET}"
7+
$ZLIB_DL_SHA512 = "1f171880153b0120e1364baaf7d0a17f65086eff279f8f8c8538e5950097d1feee37cc173181676ba1e2aeb4565ba68749c814cd3e25bfb06271bea02feb7d94"
8+
$ZLIB_DL_PATH = "${DL_BASEDIR}\${ZLIB_RELEASE_ASSET}"
9+
$CurlArguments = '-s', '-Lf', '-o', "${ZLIB_DL_PATH}", "${ZLIB_DL_URL}"
10+
& curl.exe @CurlArguments
11+
$zlib_zip_hash = Get-FileHash -LiteralPath "${ZLIB_DL_PATH}" -Algorithm SHA512
12+
if ($zlib_zip_hash.Hash -eq $ZLIB_DL_SHA512) {
13+
Write-Host "Successfully downloaded ${ZLIB_RELEASE_ASSET}"
14+
}
15+
Else {
16+
Write-Error "The downloaded ${ZLIB_RELEASE_ASSET} hash '$($zlib_zip_hash.Hash)' does not match the expected hash: '$ZLIB_DL_SHA512'"
17+
}
18+
19+
Write-Host "Extracting zlib source..."
20+
$ZLIB_SOURCE_PATH = "$env:GITHUB_WORKSPACE\buildtools\zlib-${ZLIB_RELEASE}"
21+
Expand-Archive -LiteralPath "${ZLIB_DL_PATH}" -DestinationPath "$env:GITHUB_WORKSPACE\buildtools"
22+
23+
Write-Host "Building zlib source..."
24+
$ZLIB_BUILD_PATH = "$env:GITHUB_WORKSPACE\buildtools\zlib_build"
25+
if ($env:TEST_MINGW -eq 1) {
26+
cmake.exe -B "${ZLIB_BUILD_PATH}" -S "${ZLIB_SOURCE_PATH}" -GNinja
27+
}
28+
Elseif ($env:TEST_X86 -eq 1) {
29+
cmake.exe -B "${ZLIB_BUILD_PATH}" -S "${ZLIB_SOURCE_PATH}" -AWin32
30+
}
31+
Else {
32+
cmake.exe -B "${ZLIB_BUILD_PATH}" -S "${ZLIB_SOURCE_PATH}"
33+
}
34+
cmake.exe --build "${ZLIB_BUILD_PATH}" --target zlibstatic
35+
Copy-Item "${ZLIB_SOURCE_PATH}\zlib.h" "${ZLIB_BUILD_PATH}"
36+
37+
# Append zlib CMAKE_DEFINES to the runner env.
38+
if ($env:TEST_MINGW -eq 1) {
39+
$NEW_CMAKE_DEFINES="CMAKE_DEFINES=${env:CMAKE_DEFINES} -DZLIB_LIBRARY=${ZLIB_BUILD_PATH}\libzlibstatic.a -DZLIB_INCLUDE_DIR=${ZLIB_BUILD_PATH} -GNinja"
40+
}
41+
Else {
42+
$NEW_CMAKE_DEFINES="CMAKE_DEFINES=-DZLIB_LIBRARY=${ZLIB_BUILD_PATH}\Debug\zlibstaticd.lib -DZLIB_INCLUDE_DIR=${ZLIB_BUILD_PATH}"
43+
}
44+
$NEW_CMAKE_DEFINES | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append

src/sentry_transport.c

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,8 @@ gzipped_with_compression(const char *body, const size_t body_len,
169169

170170
z_stream stream;
171171
memset(&stream, 0, sizeof(stream));
172-
stream.next_in = body;
173-
stream.avail_in = body_len;
172+
stream.next_in = (unsigned char *)body;
173+
stream.avail_in = (unsigned int)body_len;
174174

175175
int err = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
176176
MAX_WBITS + 16, 9, Z_DEFAULT_STRATEGY);
@@ -179,16 +179,16 @@ gzipped_with_compression(const char *body, const size_t body_len,
179179
return false;
180180
}
181181

182-
size_t len = compressBound(body_len);
182+
size_t len = compressBound((unsigned long)body_len);
183183
char *buffer = sentry_malloc(len);
184184
if (!buffer) {
185185
deflateEnd(&stream);
186186
return false;
187187
}
188188

189189
while (err == Z_OK) {
190-
stream.next_out = buffer + stream.total_out;
191-
stream.avail_out = len - stream.total_out;
190+
stream.next_out = (unsigned char *)(buffer + stream.total_out);
191+
stream.avail_out = (unsigned int)(len - stream.total_out);
192192
err = deflate(&stream, Z_FINISH);
193193
}
194194

@@ -225,8 +225,8 @@ sentry__prepare_http_request(sentry_envelope_t *envelope,
225225
return NULL;
226226
}
227227

228-
bool compressed = false;
229228
#ifdef SENTRY_TRANSPORT_COMPRESSION
229+
bool compressed = false;
230230
char *compressed_body = NULL;
231231
size_t compressed_body_len = 0;
232232
compressed = gzipped_with_compression(
@@ -273,11 +273,13 @@ sentry__prepare_http_request(sentry_envelope_t *envelope,
273273
h->key = "content-type";
274274
h->value = sentry__string_clone(ENVELOPE_MIME);
275275

276+
#ifdef SENTRY_TRANSPORT_COMPRESSION
276277
if (compressed) {
277278
h = &req->headers[req->headers_len++];
278279
h->key = "content-encoding";
279280
h->value = sentry__string_clone("gzip");
280281
}
282+
#endif
281283

282284
h = &req->headers[req->headers_len++];
283285
h->key = "content-length";

tests/__init__.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import gzip
12
import subprocess
23
import os
34
import io
@@ -162,10 +163,17 @@ def deserialize_from(
162163

163164
@classmethod
164165
def deserialize(
165-
cls, bytes # type: bytes
166+
cls, data # type: bytes
166167
):
167168
# type: (...) -> Envelope
168-
return cls.deserialize_from(io.BytesIO(bytes))
169+
170+
# check if the data is gzip encoded and extract it before deserialization.
171+
# 0x1f8b: gzip-magic, 0x08: `DEFLATE` compression method.
172+
if data[:3] == b"\x1f\x8b\x08":
173+
with gzip.open(io.BytesIO(data), "rb") as output:
174+
return cls.deserialize_from(output)
175+
176+
return cls.deserialize_from(io.BytesIO(data))
169177

170178
def print_verbose(self, indent=0):
171179
"""Pretty prints the envelope."""

tests/assertions.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import re
55
import sys
66
from dataclasses import dataclass
7-
from datetime import datetime
7+
from datetime import datetime, UTC
88

99
import msgpack
1010

@@ -185,7 +185,7 @@ def assert_minidump(envelope):
185185
assert minidump.payload.bytes.startswith(b"MDMP")
186186

187187

188-
def assert_timestamp(ts, now=datetime.utcnow()):
188+
def assert_timestamp(ts, now=datetime.now(UTC)):
189189
assert ts[:11] == now.isoformat()[:11]
190190

191191

@@ -307,3 +307,11 @@ def assert_crashpad_upload(req):
307307
and b"\n\nMDMP" in part.as_bytes()
308308
for part in msg.walk()
309309
)
310+
311+
312+
def assert_gzip_file_header(output):
313+
assert output[:3] == b"\x1f\x8b\x08"
314+
315+
316+
def assert_gzip_content_encoding(req):
317+
assert req.content_encoding == "gzip"

tests/cmake.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import os
21
import json
3-
import sys
2+
import os
3+
import shutil
44
import subprocess
5+
import sys
6+
57
import pytest
6-
import shutil
78

89

910
class CMake:

tests/requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
black==24.3.0
2-
pytest==8.0.1
2+
pytest==8.1.1
33
pytest-httpserver==1.0.10
44
msgpack==1.0.8
5+
pytest-xdist==3.5.0

tests/test_integration_crashpad.py

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1-
import pytest
21
import os
32
import shutil
43
import sys
54
import time
5+
6+
import pytest
7+
68
from . import make_dsn, run, Envelope
9+
from .assertions import (
10+
assert_crashpad_upload,
11+
assert_session,
12+
assert_gzip_file_header,
13+
)
714
from .conditions import has_crashpad
8-
from .assertions import assert_crashpad_upload, assert_session
915

1016
pytestmark = pytest.mark.skipif(not has_crashpad, reason="tests need crashpad backend")
1117

@@ -118,13 +124,15 @@ def test_crashpad_wer_crash(cmake, httpserver, run_args):
118124

119125

120126
@pytest.mark.parametrize(
121-
"run_args",
127+
"run_args,build_args",
122128
[
123129
# if we crash, we want a dump
124-
([]),
130+
([], {"SENTRY_TRANSPORT_COMPRESSION": "Off"}),
131+
([], {"SENTRY_TRANSPORT_COMPRESSION": "On"}),
125132
# if we crash and before-send doesn't discard, we want a dump
126133
pytest.param(
127134
["before-send"],
135+
{},
128136
marks=pytest.mark.skipif(
129137
sys.platform == "darwin",
130138
reason="crashpad doesn't provide SetFirstChanceExceptionHandler on macOS",
@@ -133,15 +141,17 @@ def test_crashpad_wer_crash(cmake, httpserver, run_args):
133141
# if on_crash() is non-discarding, a discarding before_send() is overruled, so we get a dump
134142
pytest.param(
135143
["discarding-before-send", "on-crash"],
144+
{},
136145
marks=pytest.mark.skipif(
137146
sys.platform == "darwin",
138147
reason="crashpad doesn't provide SetFirstChanceExceptionHandler on macOS",
139148
),
140149
),
141150
],
142151
)
143-
def test_crashpad_dumping_crash(cmake, httpserver, run_args):
144-
tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "crashpad"})
152+
def test_crashpad_dumping_crash(cmake, httpserver, run_args, build_args):
153+
build_args.update({"SENTRY_BACKEND": "crashpad"})
154+
tmp_path = cmake(["sentry_example"], build_args)
145155

146156
# make sure we are isolated from previous runs
147157
shutil.rmtree(tmp_path / ".sentry-native", ignore_errors=True)
@@ -169,19 +179,24 @@ def test_crashpad_dumping_crash(cmake, httpserver, run_args):
169179
run(tmp_path, "sentry_example", ["log", "no-setup"], check=True, env=env)
170180

171181
assert len(httpserver.log) == 2
172-
outputs = (httpserver.log[0][0], httpserver.log[1][0])
173182
session, multipart = (
174-
(outputs[0].get_data(), outputs[1])
175-
if b'"type":"session"' in outputs[0].get_data()
176-
else (outputs[1].get_data(), outputs[0])
183+
(httpserver.log[0][0], httpserver.log[1][0])
184+
if is_session_envelope(httpserver.log[0][0].get_data())
185+
else (httpserver.log[1][0], httpserver.log[0][0])
177186
)
178187

179-
envelope = Envelope.deserialize(session)
188+
if build_args.get("SENTRY_TRANSPORT_COMPRESSION") == "On":
189+
assert_gzip_file_header(session.get_data())
180190

191+
envelope = Envelope.deserialize(session.get_data())
181192
assert_session(envelope, {"status": "crashed", "errors": 1})
182193
assert_crashpad_upload(multipart)
183194

184195

196+
def is_session_envelope(data):
197+
return b'"type":"session"' in data
198+
199+
185200
@pytest.mark.skipif(
186201
sys.platform == "darwin",
187202
reason="crashpad doesn't provide SetFirstChanceExceptionHandler on macOS",

0 commit comments

Comments
 (0)