Skip to content

Commit f4b774c

Browse files
committed
test(aggregate): cover all of the AstarteObject functions
Signed-off-by: Joshua Chapman <joshua.chapman@secomind.com>
1 parent aab8930 commit f4b774c

File tree

3 files changed

+223
-1
lines changed

3 files changed

+223
-1
lines changed

Cargo.toml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,19 @@
22
#
33
# Copyright 2022 - 2025 SECO Mind Srl
44
#
5-
# SPDX-License-Identifier: CC0-1.0
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
# SPDX-License-Identifier: Apache-2.0
618

719
[package]
820
name = "astarte-device-sdk"

scripts/coverage.sh

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
#!/usr/bin/env bash
2+
3+
# This file is part of Astarte.
4+
#
5+
# Copyright 2025 SECO Mind Srl
6+
#
7+
# Licensed under the Apache License, Version 2.0 (the "License");
8+
# you may not use this file except in compliance with the License.
9+
# You may obtain a copy of the License at
10+
#
11+
# http://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# Unless required by applicable law or agreed to in writing, software
14+
# distributed under the License is distributed on an "AS IS" BASIS,
15+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
# See the License for the specific language governing permissions and
17+
# limitations under the License.
18+
#
19+
# SPDX-License-Identifier: Apache-2.0
20+
21+
set -exEuo pipefail
22+
23+
# Output directories for the profile files and coverage
24+
#
25+
# You'll find the coverage report and `lcov` file under: $CARGO_TARGET_DIR/debug/coverage/
26+
#
27+
# Use absolute paths every where
28+
CARGO_TARGET_DIR=$(
29+
cargo metadata --format-version 1 --no-deps --locked |
30+
jq '.target_directory' --raw-output
31+
)
32+
export CARGO_TARGET_DIR
33+
SRC_DIR="$(
34+
cargo +nightly locate-project |
35+
jq .root --raw-output |
36+
xargs dirname
37+
)"
38+
export SRC_DIR
39+
export PROFS_DIR="$CARGO_TARGET_DIR/profs"
40+
export LLVM_PROFILE_FILE="$PROFS_DIR/coverage-%p-%m.profraw"
41+
export COVERAGE_OUT_DIR="$CARGO_TARGET_DIR/debug/coverage"
42+
43+
# This require a nightly compiler
44+
#
45+
# Rustc options:
46+
#
47+
# - `instrument-coverage`: enable coverage for all
48+
# - `coverage-options=branch``: enable block and branch coverage (unstable option)
49+
#
50+
# See: https://doc.rust-lang.org/rustc/instrument-coverage.html
51+
export RUSTFLAGS="-Cinstrument-coverage -Zcoverage-options=branch"
52+
export CARGO_INCREMENTAL=0
53+
54+
# Helpful for testing changes in the generation options
55+
if [[ ${1:-} != '--no-gen' ]]; then
56+
cargo +nightly clean
57+
58+
mkdir -p "$COVERAGE_OUT_DIR"
59+
mkdir -p "$PROFS_DIR"
60+
61+
cargo +nightly test --locked --all-features --tests --no-fail-fast -p astarte-device-sdk
62+
fi
63+
64+
find_target_tool() {
65+
local libdir
66+
local tool_path
67+
68+
libdir=$(rustup run nightly rustc --print target-libdir)
69+
tool_path=$(realpath "$libdir/../bin/$1")
70+
71+
echo "$tool_path"
72+
}
73+
74+
rustup_llvm_profdata=$(find_target_tool llvm-profdata)
75+
rustup_llvm_cov=$(find_target_tool llvm-cov)
76+
77+
LLVM_PROFDATA=${LLVM_PROFDATA:-$rustup_llvm_profdata}
78+
LLVM_COV=${LLVM_COV:-$rustup_llvm_cov}
79+
80+
$LLVM_PROFDATA merge -sparse "$PROFS_DIR/"*.profraw -o "$PROFS_DIR/coverage.profdata"
81+
82+
object_files() {
83+
tests=$(
84+
cargo +nightly test --tests --all-features --no-run --message-format=json "$@" |
85+
jq -r "select(.profile.test == true) | .filenames[]" |
86+
grep -v dSYM -
87+
)
88+
89+
for file in $tests; do
90+
printf "%s %s " -object "$file"
91+
done
92+
}
93+
94+
export_lcov() {
95+
local src
96+
if [[ $1 == 'astarte-device-sdk' ]]; then
97+
src="$SRC_DIR/src"
98+
else
99+
src="$SRC_DIR/$1/src"
100+
fi
101+
102+
# shellcheck disable=2086,2046
103+
$LLVM_COV export \
104+
-format=lcov \
105+
-ignore-filename-regex='/.cargo/registry' \
106+
-instr-profile="$PROFS_DIR/coverage.profdata" \
107+
-ignore-filename-regex='.*test\.rs' \
108+
-ignore-filename-regex='.*mock\.rs' \
109+
-sources "$src" $(object_files -p "$1") \
110+
>"$COVERAGE_OUT_DIR/$1/lcov.info"
111+
}
112+
113+
filter_lcov() {
114+
local src
115+
if [[ $1 == 'astarte-device-sdk' ]]; then
116+
src="$SRC_DIR"
117+
else
118+
src="$SRC_DIR/$1"
119+
fi
120+
121+
grcov \
122+
"$COVERAGE_OUT_DIR/$1/lcov.info" \
123+
--binary-path "$CARGO_TARGET_DIR/debug" \
124+
--output-path "$COVERAGE_OUT_DIR/$1/" \
125+
--source-dir "$src" \
126+
--branch \
127+
--llvm \
128+
--excl-start 'mod test(s)?' \
129+
--output-type lcov,html
130+
}
131+
132+
list=(
133+
'astarte-device-sdk'
134+
)
135+
136+
for p in "${list[@]}"; do
137+
mkdir -p "$COVERAGE_OUT_DIR/$p/"
138+
139+
export_lcov "$p"
140+
141+
filter_lcov "$p"
142+
143+
cp -v "$COVERAGE_OUT_DIR/$p/lcov" "$COVERAGE_OUT_DIR/coverage-$p.info"
144+
145+
if [[ -n "${EXPORT_FOR_CI:-}" ]]; then
146+
cp -v "$COVERAGE_OUT_DIR/$p/lcov" "$PWD/coverage-$p.info"
147+
fi
148+
done

src/aggregate.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,16 @@ impl AstarteObject {
9393
Some(value)
9494
}
9595

96+
/// Returns the number of key values pairs in the object.
97+
pub fn len(&self) -> usize {
98+
self.inner.len()
99+
}
100+
101+
/// Returns true if the object has no values.
102+
pub fn is_empty(&self) -> bool {
103+
self.inner.is_empty()
104+
}
105+
96106
/// Iterates the name and values of the object.
97107
pub fn iter(&self) -> impl Iterator<Item = &(String, AstarteType)> {
98108
self.inner.iter()
@@ -189,6 +199,8 @@ impl Value {
189199

190200
#[cfg(test)]
191201
mod tests {
202+
use pretty_assertions::assert_eq;
203+
192204
use super::*;
193205

194206
#[test]
@@ -198,12 +210,62 @@ mod tests {
198210
assert!(val.is_individual());
199211
assert_eq!(val.as_individual(), Some(&individual));
200212
assert_eq!(val.as_object(), None);
213+
assert_eq!(val.take_individual(), Some(individual));
214+
215+
let val = Value::Individual(AstarteType::Integer(42));
216+
assert_eq!(val.take_object(), None);
201217

202218
let val = Value::Object(AstarteObject::new());
203219
assert!(val.is_object());
204220
assert_eq!(val.as_individual(), None);
205221
assert_eq!(val.as_object(), Some(&AstarteObject::new()));
222+
assert_eq!(val.take_object(), Some(AstarteObject::new()));
223+
224+
let val = Value::Object(AstarteObject::new());
225+
assert_eq!(val.take_individual(), None);
206226

207227
assert!(Value::Unset.is_unset());
208228
}
229+
230+
#[test]
231+
fn create_with_capacity() {
232+
let exp = 10;
233+
let object = AstarteObject::with_capacity(exp);
234+
assert_eq!(object.inner.capacity(), exp);
235+
}
236+
237+
#[test]
238+
fn add_value_to_obj_and_replace() {
239+
let mut object = AstarteObject::new();
240+
let exp = AstarteType::from("foo");
241+
object.insert("foo".to_string(), exp.clone());
242+
assert_eq!(object.get("foo"), Some(&exp));
243+
244+
let exp = AstarteType::from("other");
245+
object.insert("foo".to_string(), exp.clone());
246+
assert_eq!(object.get("foo"), Some(&exp));
247+
}
248+
249+
#[test]
250+
fn iter_object_values() {
251+
let values = [
252+
("foo", AstarteType::from("foo")),
253+
("bar", AstarteType::from("bar")),
254+
("some", AstarteType::from("some")),
255+
]
256+
.map(|(n, v)| (n.to_string(), v));
257+
258+
let object = AstarteObject::from_iter(values.clone());
259+
260+
assert!(!object.is_empty());
261+
assert_eq!(object.len(), values.len());
262+
263+
for (exp, val) in object.iter().zip(&values) {
264+
assert_eq!(exp, val)
265+
}
266+
267+
for (exp, val) in object.into_key_values().zip(values) {
268+
assert_eq!(exp, val)
269+
}
270+
}
209271
}

0 commit comments

Comments
 (0)