Skip to content

Commit 2232874

Browse files
authored
Adds Safety and Codecov CI (trustwallet#4408)
* Adds Rust Memory Leak Checker * FMT * Fix memory leaks * Adds codecov ci as well * Addresses review comments * Minor * Use `uname -s` * Adds dependencies install * Minor change * Moves coverage to rust ci * Minor fix * Minor * Minor * No need to install llm cov again * comment the codecov step for now
1 parent 3b6d7e8 commit 2232874

File tree

23 files changed

+185
-77
lines changed

23 files changed

+185
-77
lines changed

.github/workflows/linux-ci-rust.yml renamed to .github/workflows/rust.yml

Lines changed: 112 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Linux CI Rust
1+
name: Rust CI
22

33
on:
44
push:
@@ -16,7 +16,7 @@ concurrency:
1616

1717
jobs:
1818
# Check formatting, clippy warnings, run tests and check code coverage.
19-
build-and-test:
19+
rust-lints:
2020
permissions:
2121
contents: read
2222
checks: write
@@ -51,18 +51,6 @@ jobs:
5151
cargo clippy -- -D warnings
5252
working-directory: rust
5353

54-
- name: Run tests
55-
run: |
56-
tools/rust-coverage
57-
58-
- name: Gather and check Rust code coverage
59-
run: |
60-
tools/check-coverage rust/coverage.stats rust/coverage.info
61-
62-
- name: Run Doc tests
63-
run: |
64-
tools/rust-test doc
65-
6654
# Run Rust tests in WASM.
6755
test-wasm:
6856
runs-on: ubuntu-24.04
@@ -163,3 +151,113 @@ jobs:
163151
comment-author: 'github-actions[bot]'
164152
edit-mode: replace
165153
body-path: 'report-diff.md'
154+
155+
memory-profiler:
156+
runs-on: ubuntu-24.04
157+
if: github.event.pull_request.draft == false
158+
steps:
159+
- uses: actions/checkout@v4
160+
with:
161+
submodules: true
162+
163+
- name: Run sccache-cache
164+
uses: mozilla-actions/[email protected]
165+
166+
- name: Cache Rust
167+
uses: Swatinem/rust-cache@v2
168+
with:
169+
workspaces: |
170+
rust
171+
172+
- name: Install llvm
173+
run: |
174+
# to get the symbolizer for debug symbol resolution
175+
sudo apt install llvm
176+
177+
- name: Install nightly
178+
uses: dtolnay/rust-toolchain@nightly
179+
180+
- name: Enable debug symbols
181+
run: |
182+
cd rust
183+
# to fix buggy leak analyzer:
184+
# https://github.com/japaric/rust-san#unrealiable-leaksanitizer
185+
# ensure there's a profile.dev section
186+
if ! grep -qE '^[ \t]*[profile.dev]' Cargo.toml; then
187+
echo >> Cargo.toml
188+
echo '[profile.dev]' >> Cargo.toml
189+
fi
190+
# remove pre-existing opt-levels in profile.dev
191+
sed -i '/^\s*\[profile.dev\]/,/^\s*\[/ {/^\s*opt-level/d}' Cargo.toml
192+
# now set opt-level to 1
193+
sed -i '/^\s*\[profile.dev\]/a opt-level = 1' Cargo.toml
194+
cat Cargo.toml
195+
196+
- name: cargo test -Zsanitizer=address
197+
# only --lib --tests b/c of https://github.com/rust-lang/rust/issues/53945
198+
run: |
199+
cd rust
200+
cargo test --lib --tests --all-features --target x86_64-unknown-linux-gnu
201+
env:
202+
ASAN_OPTIONS: "detect_odr_violation=0:detect_leaks=0"
203+
RUSTFLAGS: "-Z sanitizer=address"
204+
205+
- name: cargo test -Zsanitizer=leak
206+
if: always()
207+
run: |
208+
cd rust
209+
cargo test --all-features --target x86_64-unknown-linux-gnu
210+
env:
211+
RUSTFLAGS: "-Z sanitizer=leak"
212+
213+
coverage:
214+
runs-on: ubuntu-24.04
215+
if: github.event.pull_request.draft == false
216+
217+
steps:
218+
- uses: actions/checkout@v3
219+
- name: Install system dependencies
220+
run: |
221+
tools/install-sys-dependencies-linux
222+
223+
- name: Run sccache-cache
224+
uses: mozilla-actions/[email protected]
225+
226+
- name: Cache Rust
227+
uses: Swatinem/rust-cache@v2
228+
with:
229+
workspaces: |
230+
rust
231+
232+
- name: Install Rust dependencies
233+
run: |
234+
tools/install-rust-dependencies dev
235+
236+
- name: cargo generate-lockfile
237+
if: hashFiles('Cargo.lock') == ''
238+
run: |
239+
cd rust
240+
cargo generate-lockfile
241+
242+
- name: Run tests
243+
run: |
244+
tools/rust-coverage
245+
246+
- name: Run Doc tests
247+
run: |
248+
tools/rust-test doc
249+
250+
- name: Record Rust version
251+
run: echo "RUST=$(rustc --version)" >> "$GITHUB_ENV"
252+
253+
# TODO: Uncomment this when we have a codecov token
254+
# - name: Upload to codecov.io
255+
# uses: codecov/codecov-action@v5
256+
# with:
257+
# fail_ci_if_error: true
258+
# token: ${{ secrets.CODECOV_TOKEN }}
259+
# env_vars: OS,RUST
260+
261+
- name: Gather and check Rust code coverage
262+
run: |
263+
tools/check-coverage rust/coverage.stats rust/lcov.info

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ emsdk/
5757
wasm-build/
5858

5959
# Code coverage files
60+
lcov.info
6061
coverage.info
6162
coverage/
6263
swift/test_output/

android/gradlew

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ cygwin=false
104104
msys=false
105105
darwin=false
106106
nonstop=false
107-
case "$( uname )" in #(
107+
case "$( uname -s )" in #(
108108
CYGWIN* ) cygwin=true ;; #(
109109
Darwin* ) darwin=true ;; #(
110110
MSYS* | MINGW* ) msys=true ;; #(

bootstrap.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ if isHelp; then
1515
fi
1616

1717
echo "#### Installing system dependencies ... ####"
18-
if [[ $(uname) == "Darwin" ]]; then
18+
if [[ $(uname -s) == "Darwin" ]]; then
1919
tools/install-sys-dependencies-mac
2020
else
2121
tools/install-sys-dependencies-linux

kotlin/gradlew

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ cygwin=false
108108
msys=false
109109
darwin=false
110110
nonstop=false
111-
case "$( uname )" in #(
111+
case "$( uname -s )" in #(
112112
CYGWIN* ) cygwin=true ;; #(
113113
Darwin* ) darwin=true ;; #(
114114
MSYS* | MINGW* ) msys=true ;; #(

rust/tw_encoding/tests/base32_ffi_tests.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ use tw_encoding::ffi::{decode_base32, encode_base32};
99
/// equals to `expected`.
1010
#[track_caller]
1111
fn test_base32_encode_helper(input: &[u8], expected: &str, alphabet: Option<&str>, padding: bool) {
12-
let alphabet = alphabet
13-
.map(|alphabet| CString::new(alphabet).unwrap().into_raw())
14-
.unwrap_or_else(std::ptr::null_mut);
12+
let alphabet_cstring = alphabet.map(|alphabet| CString::new(alphabet).unwrap());
13+
let alphabet_ptr = alphabet_cstring
14+
.as_ref()
15+
.map(|s| s.as_ptr())
16+
.unwrap_or_else(std::ptr::null);
1517

1618
let result_ptr =
17-
unsafe { encode_base32(input.as_ptr(), input.len(), alphabet, padding) }.unwrap();
19+
unsafe { encode_base32(input.as_ptr(), input.len(), alphabet_ptr, padding) }.unwrap();
1820
let result = unsafe { CString::from_raw(result_ptr) };
1921
assert_eq!(result.to_str().unwrap(), expected);
2022
}
@@ -24,12 +26,14 @@ fn test_base32_encode_helper(input: &[u8], expected: &str, alphabet: Option<&str
2426
#[track_caller]
2527
fn test_base32_decode_helper(input: &str, expected: &[u8], alphabet: Option<&str>, padding: bool) {
2628
let input = CString::new(input).unwrap();
27-
let alphabet = alphabet
28-
.map(|alphabet| CString::new(alphabet).unwrap().into_raw())
29-
.unwrap_or_else(std::ptr::null_mut);
29+
let alphabet_cstring = alphabet.map(|alphabet| CString::new(alphabet).unwrap());
30+
let alphabet_ptr = alphabet_cstring
31+
.as_ref()
32+
.map(|s| s.as_ptr())
33+
.unwrap_or_else(std::ptr::null);
3034

3135
let decoded = unsafe {
32-
decode_base32(input.as_ptr(), alphabet, padding)
36+
decode_base32(input.as_ptr(), alphabet_ptr, padding)
3337
.unwrap()
3438
.into_vec()
3539
};

rust/tw_encoding/tests/base64_ffi_tests.rs

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,25 @@
22
//
33
// Copyright © 2017 Trust Wallet.
44

5-
use std::ffi::{CStr, CString};
5+
use std::ffi::CString;
66
use tw_encoding::ffi::{decode_base64, encode_base64};
77

88
#[test]
99
fn test_encode_base64() {
1010
let data = b"hello world";
11-
let encoded = unsafe { CStr::from_ptr(encode_base64(data.as_ptr(), data.len(), false)) };
11+
let result_ptr = unsafe { encode_base64(data.as_ptr(), data.len(), false) };
12+
let result = unsafe { CString::from_raw(result_ptr) };
1213
let expected = "aGVsbG8gd29ybGQ=";
13-
assert_eq!(encoded.to_str().unwrap(), expected);
14+
assert_eq!(result.to_str().unwrap(), expected);
1415
}
1516

1617
#[test]
1718
fn test_encode_base64_url() {
1819
let data = b"+'?ab";
19-
let encoded = unsafe { CStr::from_ptr(encode_base64(data.as_ptr(), data.len(), true)) };
20+
let result_ptr = unsafe { encode_base64(data.as_ptr(), data.len(), true) };
21+
let result = unsafe { CString::from_raw(result_ptr) };
2022
let expected = "Kyc_YWI=";
21-
assert_eq!(encoded.to_str().unwrap(), expected);
23+
assert_eq!(result.to_str().unwrap(), expected);
2224
}
2325

2426
#[test]
@@ -27,9 +29,11 @@ fn test_decode_base64_url() {
2729
let expected = b"+'?ab";
2830

2931
let encoded_c_str = CString::new(encoded).unwrap();
30-
let encoded_ptr = encoded_c_str.as_ptr();
31-
32-
let decoded = unsafe { decode_base64(encoded_ptr, true).unwrap().into_vec() };
32+
let decoded = unsafe {
33+
decode_base64(encoded_c_str.as_ptr(), true)
34+
.unwrap()
35+
.into_vec()
36+
};
3337
assert_eq!(decoded, expected);
3438
}
3539

@@ -39,17 +43,18 @@ fn test_decode_base64() {
3943
let expected = b"hello world!";
4044

4145
let encoded_c_str = CString::new(encoded).unwrap();
42-
let encoded_ptr = encoded_c_str.as_ptr();
43-
44-
let decoded = unsafe { decode_base64(encoded_ptr, false).unwrap().into_vec() };
46+
let decoded = unsafe {
47+
decode_base64(encoded_c_str.as_ptr(), false)
48+
.unwrap()
49+
.into_vec()
50+
};
4551
assert_eq!(decoded, expected);
4652
}
4753

4854
#[test]
4955
fn test_decode_base64_invalid() {
5056
let invalid_encoded = "_This_is_an_invalid_base64_";
5157
let encoded_c_str = CString::new(invalid_encoded).unwrap();
52-
let encoded_ptr = encoded_c_str.as_ptr();
53-
let res = unsafe { decode_base64(encoded_ptr, false) };
58+
let res = unsafe { decode_base64(encoded_c_str.as_ptr(), false) };
5459
assert!(res.is_err());
5560
}

rust/tw_encoding/tests/hex_ffi_tests.rs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,33 @@
22
//
33
// Copyright © 2017 Trust Wallet.
44

5-
use std::ffi::{CStr, CString};
5+
use std::ffi::CString;
66
use tw_encoding::ffi::{decode_hex, encode_hex};
77

88
#[test]
99
fn test_encode_hex_without_prefix() {
1010
let data = b"hello world";
11-
let encoded = unsafe { CStr::from_ptr(encode_hex(data.as_ptr(), data.len(), false)) };
11+
let result_ptr = unsafe { encode_hex(data.as_ptr(), data.len(), false) };
12+
let result = unsafe { CString::from_raw(result_ptr) };
1213
let expected = "68656c6c6f20776f726c64";
13-
assert_eq!(encoded.to_str().unwrap(), expected);
14+
assert_eq!(result.to_str().unwrap(), expected);
1415
}
1516

1617
#[test]
1718
fn test_encode_hex_with_prefix() {
1819
let data = b"hello world";
19-
let encoded = unsafe { CStr::from_ptr(encode_hex(data.as_ptr(), data.len(), true)) };
20+
let result_ptr = unsafe { encode_hex(data.as_ptr(), data.len(), true) };
21+
let result = unsafe { CString::from_raw(result_ptr) };
2022
let expected = "0x68656c6c6f20776f726c64";
21-
assert_eq!(encoded.to_str().unwrap(), expected);
23+
assert_eq!(result.to_str().unwrap(), expected);
2224
}
2325

2426
#[test]
2527
fn test_decode_hex() {
2628
let encoded = "68656c6c6f20776f726c64";
2729

2830
let encoded_c_str = CString::new(encoded).unwrap();
29-
let encoded_ptr = encoded_c_str.as_ptr();
30-
31-
let decoded: Vec<_> = unsafe { decode_hex(encoded_ptr).unwrap().into_vec() };
31+
let decoded: Vec<_> = unsafe { decode_hex(encoded_c_str.as_ptr()).unwrap().into_vec() };
3232
assert_eq!(decoded, b"hello world");
3333
}
3434

@@ -37,8 +37,6 @@ fn test_decode_hex_with_prefix() {
3737
let encoded = "0x68656c6c6f20776f726c64";
3838

3939
let encoded_c_str = CString::new(encoded).unwrap();
40-
let encoded_ptr = encoded_c_str.as_ptr();
41-
42-
let decoded: Vec<_> = unsafe { decode_hex(encoded_ptr).unwrap().into_vec() };
40+
let decoded: Vec<_> = unsafe { decode_hex(encoded_c_str.as_ptr()).unwrap().into_vec() };
4341
assert_eq!(decoded, b"hello world");
4442
}

rust/tw_tests/tests/utils/bit_reader.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use tw_encoding::hex::{DecodeHex, ToHex};
66
use tw_memory::test_utils::tw_data_helper::TWDataHelper;
77
use wallet_core_rs::ffi::utils::bit_reader_ffi::{
8-
tw_bit_reader_create, tw_bit_reader_finished, tw_bit_reader_read_u8,
8+
tw_bit_reader_create, tw_bit_reader_delete, tw_bit_reader_finished, tw_bit_reader_read_u8,
99
tw_bit_reader_read_u8_slice, CBitReaderCode,
1010
};
1111

@@ -37,6 +37,8 @@ fn test_tw_bit_reader_success() {
3737
);
3838

3939
assert!(unsafe { tw_bit_reader_finished(reader) });
40+
41+
unsafe { tw_bit_reader_delete(reader) };
4042
}
4143

4244
#[test]
@@ -66,4 +68,6 @@ fn test_tw_bit_reader_error() {
6668
res.into_result().unwrap_err(),
6769
CBitReaderCode::NotEnoughData as i32
6870
);
71+
72+
unsafe { tw_bit_reader_delete(reader) };
6973
}

0 commit comments

Comments
 (0)