Skip to content

Commit d52a096

Browse files
committed
[#31]:svarga:backend, PB fixture tests with .proto golden baselines
1 parent a53ea91 commit d52a096

36 files changed

Lines changed: 879 additions & 42 deletions

tests/CMakeLists.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ endfunction()
3232

3333
function(add_h5cpp_backend_fixture_test name backend)
3434
set(golden "${GOLDEN_DIR}/${name}.${backend}.expected")
35+
set(proto_golden "${GOLDEN_DIR}/${name}.proto.expected")
36+
set(proto_arg "")
37+
if(EXISTS "${proto_golden}")
38+
set(proto_arg "-DPROTO_GOLDEN=${proto_golden}")
39+
endif()
3540
add_test(
3641
NAME h5cpp.${name}
3742
COMMAND
@@ -42,6 +47,7 @@ function(add_h5cpp_backend_fixture_test name backend)
4247
"-DGOLDEN=${golden}"
4348
"-DBACKEND_FORMAT=${backend}"
4449
"-DOUTPUT_DIR=${CMAKE_CURRENT_BINARY_DIR}"
50+
${proto_arg}
4551
-P "${RUN_FIXTURE}"
4652
)
4753
endfunction()
@@ -57,6 +63,14 @@ set(PB_FIXTURES
5763
pb_variant
5864
pb_wire_specs
5965
pb_chrono
66+
pb_enum_tag
67+
pb_ignore
68+
pb_universals
69+
pb_tier2
70+
pb_enum_decl
71+
pb_tier3a
72+
pb_service
73+
pb_clean_form
6074
)
6175

6276
foreach(fixture IN LISTS PB_FIXTURES)

tests/fixtures/pb_chrono.cpp

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,18 @@
66
// Tier-4 fixture (stage 6): chrono adapter annotation. A user struct with
77
// std::chrono::system_clock::time_point + std::chrono::nanoseconds members
88
// gets pb::adapter_field<N, Ptr, pb::Timestamp_adapter> / Duration_adapter
9-
// entries in the emitted descriptor. The library bridges the C++ types
10-
// to proto3 google.protobuf.Timestamp / Duration wire messages.
9+
// entries in the emitted descriptor. The library bridges the C++ types to
10+
// proto3 google.protobuf.Timestamp / Duration wire messages.
1111
namespace sn::pb_test {
1212

1313
struct event_t {
14-
[[clang::annotate("pb::field=1")]]
14+
[[pb::field(1)]]
1515
std::int64_t id;
1616

17-
[[clang::annotate("pb::field=2")]]
18-
[[clang::annotate("pb::adapter=Timestamp")]]
17+
[[pb::field(2), pb::adapter("Timestamp")]]
1918
std::chrono::system_clock::time_point when;
2019

21-
[[clang::annotate("pb::field=3")]]
22-
[[clang::annotate("pb::adapter=Duration")]]
20+
[[pb::field(3), pb::adapter("Duration")]]
2321
std::chrono::nanoseconds ttl;
2422
};
2523

tests/fixtures/pb_clean_form.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#include "pb_stub.hpp"
2+
3+
#include <chrono>
4+
#include <cstdint>
5+
#include <map>
6+
#include <string>
7+
#include <variant>
8+
9+
// Issue #31 Phase 1b — clean `[[pb::field(N)]]` syntax delivered by the
10+
// source-level rewriter (src/pb_attr_translator.hpp).
11+
//
12+
// No `clang::annotate(...)` envelope; users write attributes directly in
13+
// the pb:: namespace. Tag arguments may be integer literals OR enum
14+
// values OR any mix — the rewriter lowers everything to clang::annotate
15+
// before Clang's AST walker sees it, and Expr::EvaluateAsInt resolves
16+
// both kinds in one code path.
17+
18+
namespace acme::telemetry {
19+
20+
enum class telemetry_tag : std::uint32_t {
21+
captured_at = 1,
22+
elapsed = 2,
23+
device_id = 3,
24+
samples = 4,
25+
payload = 5, // base for oneof: 5/6/7
26+
};
27+
28+
struct telemetry_event_t {
29+
[[pb::field(telemetry_tag::captured_at), pb::adapter("Timestamp")]]
30+
std::chrono::system_clock::time_point captured_at;
31+
32+
[[pb::field(telemetry_tag::elapsed), pb::adapter("Duration")]]
33+
std::chrono::nanoseconds elapsed;
34+
35+
[[pb::field(telemetry_tag::device_id)]]
36+
std::string device_id;
37+
38+
[[pb::field(telemetry_tag::samples)]]
39+
std::map<std::string, double> samples;
40+
41+
[[pb::field(telemetry_tag::payload,
42+
static_cast<std::uint32_t>(telemetry_tag::payload) + 1u,
43+
static_cast<std::uint32_t>(telemetry_tag::payload) + 2u)]]
44+
std::variant<std::monostate,
45+
std::string, // text alert (tag 5)
46+
std::int64_t, // event code (tag 6)
47+
double> // metric (tag 7)
48+
payload;
49+
50+
[[pb::field(10), pb::wire("sint32")]]
51+
std::int32_t delta; // zigzag, often negative
52+
53+
[[pb::field(11)]]
54+
std::int32_t legacy_count; // raw int next to enum-tagged fields
55+
56+
[[pb::ignore]]
57+
void* runtime_handle; // cache; never persisted to wire
58+
};
59+
60+
} // namespace acme::telemetry
61+
62+
void use() {
63+
acme::telemetry::telemetry_event_t e{};
64+
auto buf = pb::encode(e);
65+
(void)buf;
66+
}

tests/fixtures/pb_composites.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@
1515
namespace sn::pb_test {
1616

1717
struct profile_t {
18-
[[clang::annotate("pb::field=1")]] std::string name;
19-
[[clang::annotate("pb::field=2")]] std::vector<std::int32_t> scores;
20-
[[clang::annotate("pb::field=3")]] std::optional<std::string> nickname;
21-
[[clang::annotate("pb::field=4")]] std::vector<std::string> aliases;
22-
[[clang::annotate("pb::field=5")]] std::optional<std::int64_t> user_id;
18+
[[pb::field(1)]] std::string name;
19+
[[pb::field(2)]] std::vector<std::int32_t> scores;
20+
[[pb::field(3)]] std::optional<std::string> nickname;
21+
[[pb::field(4)]] std::vector<std::string> aliases;
22+
[[pb::field(5)]] std::optional<std::int64_t> user_id;
2323
};
2424

2525
} // namespace sn::pb_test

tests/fixtures/pb_enum_decl.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#include "pb_stub.hpp"
2+
3+
#include <cstdint>
4+
5+
// Commit 1: proto3 `enum Foo { VAL = N; ... }` declaration emission.
6+
//
7+
// Three scenarios in one fixture:
8+
// 1. `color_e` — has an UNSPECIFIED = 0 entry already. Clean emission;
9+
// no diagnostic.
10+
// 2. `priority_e` — no zero-valued enumerator BUT carries
11+
// [[pb::enum_zero("UNSPECIFIED")]]. h5cpp synthesizes
12+
// `UNSPECIFIED = 0;` as the first line of the proto3
13+
// enum block. The C++ enum is unchanged.
14+
// 3. `direction_e` — uses [[pb::name(...)]] to override the proto3
15+
// message name. Already has a zero entry.
16+
17+
namespace sn::pb_test {
18+
19+
enum class color_e : std::int32_t {
20+
UNSPECIFIED = 0,
21+
RED = 1,
22+
GREEN = 2,
23+
BLUE = 3,
24+
};
25+
26+
// Proto3 requires the first entry to be 0-valued; this C++ enum has no
27+
// zero. The pb::enum_zero override synthesizes one in the .proto output.
28+
enum class [[pb::enum_zero("PRIORITY_UNSPECIFIED")]] priority_e : std::int32_t {
29+
LOW = 1,
30+
NORMAL = 2,
31+
HIGH = 3,
32+
};
33+
34+
enum class [[pb::name("Heading"), pb::doc("compass headings")]] direction_e : std::int32_t {
35+
HEADING_UNSPECIFIED = 0,
36+
NORTH = 1,
37+
EAST = 2,
38+
SOUTH = 3,
39+
WEST = 4,
40+
};
41+
42+
struct event_t {
43+
[[pb::field(1)]] color_e color;
44+
[[pb::field(2)]] priority_e priority;
45+
[[pb::field(3)]] direction_e heading;
46+
};
47+
48+
} // namespace sn::pb_test
49+
50+
void use() {
51+
sn::pb_test::event_t e{};
52+
auto buf = pb::encode(e);
53+
(void)buf;
54+
}

tests/fixtures/pb_enum_tag.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#include "pb_stub.hpp"
2+
3+
#include <cstdint>
4+
#include <map>
5+
#include <string>
6+
#include <variant>
7+
8+
// Issue #31 headline feature: enum-typed tag arguments.
9+
//
10+
// Tag numbers come from a named enum rather than scattered integer literals.
11+
// The compiler evaluates each enum reference via Expr::EvaluateAsInt(ctx)
12+
// and emits the same pb::field<N, Ptr>{} descriptor as if the user had
13+
// written the raw number. Mixed enum + int args allowed (see `legacy_count`
14+
// which uses a raw integer right alongside the enum-tagged fields).
15+
//
16+
// Permissive default per the attribute taxonomy doc §6 — no class-level
17+
// "must use this enum" binding required; the library doesn't choose the route.
18+
19+
namespace sn::pb_test {
20+
21+
enum class user_profile_tag : std::uint32_t {
22+
name = 1,
23+
scores = 2,
24+
flags = 3,
25+
payload = 5, // base for oneof — alts at 5, 6, 7
26+
};
27+
28+
struct user_profile_t {
29+
[[pb::field(user_profile_tag::name)]]
30+
std::string name;
31+
32+
[[pb::field(user_profile_tag::scores)]]
33+
std::map<std::string, std::int32_t> scores;
34+
35+
[[pb::field(user_profile_tag::flags)]]
36+
std::map<bool, std::string> flags;
37+
38+
// Variadic enum-tagged oneof. Three alternatives mapped to a contiguous
39+
// tag range. Compiler reads each Expr* via EvaluateAsInt → integer 5/6/7.
40+
[[pb::field(user_profile_tag::payload,
41+
static_cast<std::uint32_t>(user_profile_tag::payload) + 1u,
42+
static_cast<std::uint32_t>(user_profile_tag::payload) + 2u)]]
43+
std::variant<std::monostate, std::string, std::int64_t, double> payload;
44+
45+
// Mixed-form: raw integer alongside enum-tagged fields. Permissive default
46+
// accepts the mix without diagnostic.
47+
[[pb::field(10)]]
48+
std::int32_t legacy_count;
49+
};
50+
51+
} // namespace sn::pb_test
52+
53+
void use() {
54+
sn::pb_test::user_profile_t u{};
55+
auto buf = pb::encode(u);
56+
(void)buf;
57+
}

tests/fixtures/pb_ignore.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#include "pb_stub.hpp"
2+
3+
#include <cstdint>
4+
#include <string>
5+
6+
// FR12: [[pb::ignore]] marks a non-static data member as deliberately
7+
// excluded from wire encoding (caches, derived state, locks, runtime
8+
// handles, etc.). The compiler emits pb::ignore<&T::m>{} in the
9+
// descriptor — required so CCC#7 descriptor-completeness recognizes the
10+
// member as deliberately omitted rather than accidentally forgotten.
11+
12+
namespace sn::pb_test {
13+
14+
struct record_with_ignore_t {
15+
[[pb::field(1)]] std::string name;
16+
[[pb::ignore]] void* runtime_handle; // cache; never wire
17+
[[pb::field(2)]] std::int32_t score;
18+
[[pb::ignore]] std::int64_t derived_counter; // computed at runtime
19+
[[pb::field(3)]] std::string description;
20+
};
21+
22+
} // namespace sn::pb_test
23+
24+
void use() {
25+
sn::pb_test::record_with_ignore_t r{};
26+
auto buf = pb::encode(r);
27+
(void)buf;
28+
}

tests/fixtures/pb_primitives.cpp

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,18 @@
22

33
#include <cstdint>
44

5-
// Tier-1 fixture: numeric scalars only. Every member carries
6-
// [[clang::annotate("pb::field=N")]]; the compiler emits a
7-
// pb::meta::descriptor_t<T> specialization with one pb::field<N,&T::m>{}
8-
// entry per member, in source-declaration order.
5+
// Tier-1 fixture: numeric scalars only. Every member carries [[pb::field(N)]];
6+
// the compiler emits a pb::meta::descriptor_t<T> specialization with one
7+
// pb::field<N, &T::m>{} entry per member, in source-declaration order.
98
namespace sn::pb_test {
109
struct primitives_t {
11-
[[clang::annotate("pb::field=1")]] bool _bool;
12-
[[clang::annotate("pb::field=2")]] std::int32_t _i32;
13-
[[clang::annotate("pb::field=3")]] std::int64_t _i64;
14-
[[clang::annotate("pb::field=4")]] std::uint32_t _u32;
15-
[[clang::annotate("pb::field=5")]] std::uint64_t _u64;
16-
[[clang::annotate("pb::field=6")]] float _f;
17-
[[clang::annotate("pb::field=7")]] double _d;
10+
[[pb::field(1)]] bool _bool;
11+
[[pb::field(2)]] std::int32_t _i32;
12+
[[pb::field(3)]] std::int64_t _i64;
13+
[[pb::field(4)]] std::uint32_t _u32;
14+
[[pb::field(5)]] std::uint64_t _u64;
15+
[[pb::field(6)]] float _f;
16+
[[pb::field(7)]] double _d;
1817
};
1918
} // namespace sn::pb_test
2019

tests/fixtures/pb_service.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#include "pb_stub.hpp"
2+
3+
#include <cstdint>
4+
#include <functional>
5+
#include <string>
6+
7+
// Commit 4: [[pb::service("Name")]] — a record marked this way emits as
8+
// a proto3 `service Name { ... }` block. Each std::function<Resp(Req)>
9+
// member becomes one `rpc <member-name>(Req) returns (Resp);` line.
10+
// Non-function fields would be skipped with a diagnostic; this fixture
11+
// keeps the struct RPC-handler-only.
12+
//
13+
// The Request / Response struct types are walked as dependencies so
14+
// they emit as proto3 `message` blocks BEFORE the service block.
15+
16+
namespace acme::users {
17+
18+
// Request / response messages. Plain pb::field-only structs.
19+
struct [[pb::doc("user-id lookup request")]] get_user_req_t {
20+
[[pb::field(1)]] std::int64_t user_id;
21+
};
22+
struct [[pb::doc("user-id lookup response")]] get_user_resp_t {
23+
[[pb::field(1)]] std::string display_name;
24+
[[pb::field(2)]] std::int64_t created_ns;
25+
};
26+
27+
struct list_users_req_t {
28+
[[pb::field(1)]] std::int32_t page;
29+
[[pb::field(2)]] std::int32_t page_size;
30+
};
31+
struct list_users_resp_t {
32+
[[pb::field(1)]] std::int32_t total;
33+
};
34+
35+
struct auth_req_t { [[pb::field(1)]] std::string token; };
36+
struct auth_resp_t { [[pb::field(1)]] bool ok; };
37+
38+
// The service definition. No data fields — only RPC handlers.
39+
struct [[pb::service("UserService"),
40+
pb::doc("user-account RPC surface")]]
41+
user_service_t {
42+
std::function<get_user_resp_t(get_user_req_t)> get_user;
43+
std::function<list_users_resp_t(list_users_req_t)> list_users;
44+
std::function<auth_resp_t(auth_req_t)> authenticate;
45+
};
46+
47+
} // namespace acme::users
48+
49+
void use() {
50+
// Touching the messages teaches h5cpp-compiler the requests/responses
51+
// exist as user records — they emit as `message` blocks before the
52+
// service that references them.
53+
acme::users::get_user_req_t q1{}; (void)pb::encode(q1);
54+
acme::users::get_user_resp_t r1{}; (void)pb::encode(r1);
55+
acme::users::list_users_req_t q2{}; (void)pb::encode(q2);
56+
acme::users::list_users_resp_t r2{}; (void)pb::encode(r2);
57+
acme::users::auth_req_t q3{}; (void)pb::encode(q3);
58+
acme::users::auth_resp_t r3{}; (void)pb::encode(r3);
59+
acme::users::user_service_t svc{}; (void)pb::encode(svc);
60+
}

tests/fixtures/pb_strings_enums.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ enum class severity_e : std::int32_t {
1717
};
1818

1919
struct log_entry_t {
20-
[[clang::annotate("pb::field=1")]] std::int64_t timestamp_ns;
21-
[[clang::annotate("pb::field=2")]] std::string message;
22-
[[clang::annotate("pb::field=3")]] severity_e level;
23-
[[clang::annotate("pb::field=4")]] double elapsed_ms;
20+
[[pb::field(1)]] std::int64_t timestamp_ns;
21+
[[pb::field(2)]] std::string message;
22+
[[pb::field(3)]] severity_e level;
23+
[[pb::field(4)]] double elapsed_ms;
2424
};
2525

2626
} // namespace sn::pb_test

0 commit comments

Comments
 (0)