Skip to content

Commit 94521f0

Browse files
committed
style(input): clear SonarQube code smells on the new keyboard code
- Return the test keyboard recorder from an accessor, so the file adds no non-const variable at namespace scope - Build assertion tokens and the stream name with std::format instead of output manipulators and manual concatenation - Hold modifier masks in unsigned, and cast once where the packet helper needs a byte - Move the modifier selection loop into select_modifiers(), which drops the combination test to three levels of nesting - Make the fixture stream private, add using enum for side_e, and use std::ranges::sort with class template argument deduction
1 parent c81a7c1 commit 94521f0

2 files changed

Lines changed: 83 additions & 50 deletions

File tree

src/input.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,15 @@ namespace input {
166166

167167
static platf::input_t platf_input;
168168
#ifdef SUNSHINE_TESTS
169-
static std::function<void(const testing::keyboard_event_t &)> keyboard_sink {};
169+
/**
170+
* @brief Recorder that unit tests install in place of the platform keyboard.
171+
*
172+
* @return Mutable reference to the recorder, empty when no test installed one.
173+
*/
174+
std::function<void(const testing::keyboard_event_t &)> &keyboard_sink() {
175+
static std::function<void(const testing::keyboard_event_t &)> sink;
176+
return sink;
177+
}
170178
#endif
171179
static std::bitset<platf::MAX_GAMEPADS> gamepadMask {};
172180

@@ -992,8 +1000,8 @@ namespace input {
9921000
*/
9931001
void emit_keyboard_update(uint16_t key_code, bool release, uint8_t flags) {
9941002
#ifdef SUNSHINE_TESTS
995-
if (keyboard_sink) {
996-
keyboard_sink(testing::keyboard_event_t {key_code, release, flags});
1003+
if (keyboard_sink()) {
1004+
keyboard_sink()(testing::keyboard_event_t {key_code, release, flags});
9971005
return;
9981006
}
9991007
#endif
@@ -2186,7 +2194,7 @@ namespace input {
21862194
}
21872195

21882196
void set_keyboard_sink(std::function<void(const keyboard_event_t &)> sink) {
2189-
keyboard_sink = std::move(sink);
2197+
keyboard_sink() = std::move(sink);
21902198
}
21912199

21922200
void send_keyboard_packet(std::shared_ptr<input_t> &input, std::uint16_t key_code, std::uint8_t modifiers, std::uint8_t flags, bool release) {

tests/unit/test_keyboard.cpp

Lines changed: 71 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,9 @@
2222
#include <array>
2323
#include <chrono>
2424
#include <cstdint>
25-
#include <iomanip>
25+
#include <format>
2626
#include <memory>
2727
#include <set>
28-
#include <sstream>
2928
#include <string>
3029
#include <utility>
3130
#include <vector>
@@ -192,7 +191,7 @@ namespace {
192191
* @brief One modifier the client can report, with the keys that hold it down.
193192
*/
194193
struct modifier_t {
195-
std::uint8_t bit; ///< Modifier bit carried in the keyboard packet.
194+
unsigned bit; ///< Modifier bit carried in the keyboard packet.
196195
std::uint16_t left; ///< Left-hand virtual-key code.
197196
std::uint16_t right; ///< Right-hand virtual-key code.
198197
std::uint16_t generic; ///< Side-agnostic virtual-key code.
@@ -224,27 +223,59 @@ namespace {
224223
* @return Virtual-key code the client sends.
225224
*/
226225
std::uint16_t key_for_side(const modifier_t &modifier, side_e side) {
226+
using enum side_e;
227227
switch (side) {
228-
case side_e::left:
228+
case left:
229229
return modifier.left;
230-
case side_e::right:
230+
case right:
231231
return modifier.right;
232-
case side_e::generic:
232+
case generic:
233233
default:
234234
return modifier.generic;
235235
}
236236
}
237237

238+
/**
239+
* @brief One modifier combination the client holds down, in press order.
240+
*/
241+
struct held_modifiers_t {
242+
std::vector<std::pair<std::uint16_t, unsigned>> keys; ///< Key code and the mask reported with it.
243+
unsigned claimed = 0; ///< Full mask once every key is down.
244+
std::string label; ///< Combination name for assertion traces.
245+
};
246+
247+
/**
248+
* @brief Select the modifier keys named by a combination bitmask.
249+
*
250+
* Each key carries the mask accumulated so far, which matches a client that presses the
251+
* modifiers one at a time.
252+
*
253+
* @param combination Bit per entry of the modifier table.
254+
* @param side Which physical side the client reports.
255+
* @return Keys to hold, the resulting mask, and a trace label.
256+
*/
257+
held_modifiers_t select_modifiers(unsigned combination, side_e side) {
258+
held_modifiers_t held;
259+
for (std::size_t index = 0; index < modifiers.size(); ++index) {
260+
if (!(combination & (1u << index))) {
261+
continue;
262+
}
263+
held.claimed |= modifiers[index].bit;
264+
held.keys.emplace_back(key_for_side(modifiers[index], side), held.claimed);
265+
held.label += (held.label.empty() ? "" : "+");
266+
held.label += modifiers[index].name;
267+
}
268+
return held;
269+
}
270+
238271
/**
239272
* @brief Render a value as a hexadecimal literal for assertion traces.
240273
*
241274
* @param value Value to render.
242275
* @return Hexadecimal literal.
243276
*/
244277
std::string hex(unsigned value) {
245-
std::ostringstream out;
246-
out << "0x" << std::uppercase << std::hex << value;
247-
return out.str();
278+
return std::format("0x{:X}", value);
248279
}
249280

250281
/**
@@ -258,11 +289,7 @@ namespace {
258289
* @return Token describing the event.
259290
*/
260291
std::string describe(const input::testing::keyboard_event_t &event) {
261-
std::ostringstream token;
262-
token << (event.release ? '-' : '+')
263-
<< "0x" << std::uppercase << std::hex << std::setw(2) << std::setfill('0')
264-
<< event.key_code;
265-
return token.str();
292+
return std::format("{}0x{:02X}", event.release ? '-' : '+', event.key_code);
266293
}
267294

268295
/**
@@ -315,7 +342,7 @@ namespace {
315342
});
316343

317344
static int session = 0;
318-
stream_ = input::alloc(std::make_shared<safe::mail_raw_t>(), "keyboard-test-" + std::to_string(++session));
345+
stream_ = input::alloc(std::make_shared<safe::mail_raw_t>(), std::format("keyboard-test-{}", ++session));
319346
ASSERT_NE(stream_, nullptr);
320347
}
321348

@@ -338,8 +365,14 @@ namespace {
338365
* @param client_modifiers Modifier bitmask the client reports as held.
339366
* @param flags Bit flags carried by the packet.
340367
*/
341-
void press(std::uint16_t key_code, std::uint8_t client_modifiers = 0, std::uint8_t flags = 0) {
342-
input::testing::send_keyboard_packet(stream_, key_code, client_modifiers, flags, false);
368+
void press(std::uint16_t key_code, unsigned client_modifiers = 0, unsigned flags = 0) {
369+
input::testing::send_keyboard_packet(
370+
stream_,
371+
key_code,
372+
static_cast<std::uint8_t>(client_modifiers),
373+
static_cast<std::uint8_t>(flags),
374+
false
375+
);
343376
}
344377

345378
/**
@@ -349,8 +382,14 @@ namespace {
349382
* @param client_modifiers Modifier bitmask the client reports as held.
350383
* @param flags Bit flags carried by the packet.
351384
*/
352-
void release(std::uint16_t key_code, std::uint8_t client_modifiers = 0, std::uint8_t flags = 0) {
353-
input::testing::send_keyboard_packet(stream_, key_code, client_modifiers, flags, true);
385+
void release(std::uint16_t key_code, unsigned client_modifiers = 0, unsigned flags = 0) {
386+
input::testing::send_keyboard_packet(
387+
stream_,
388+
key_code,
389+
static_cast<std::uint8_t>(client_modifiers),
390+
static_cast<std::uint8_t>(flags),
391+
true
392+
);
354393
}
355394

356395
/**
@@ -386,9 +425,8 @@ namespace {
386425
return *context_->keyboard;
387426
}
388427

389-
std::shared_ptr<input::input_t> stream_; ///< Stream input state under test.
390-
391428
private:
429+
std::shared_ptr<input::input_t> stream_; ///< Stream input state under test.
392430
std::vector<input::testing::keyboard_event_t> events_; ///< Keyboard output recorded for the current test.
393431
platf::virtualhid::input_context_t *context_ = nullptr; ///< Fake input context installed in the global backend.
394432
config::input_t original_input_; ///< Input configuration restored after each test.
@@ -469,35 +507,22 @@ TEST_F(KeyboardPassthroughTest, HoldsEveryModifierCombinationWithoutSyntheticInj
469507
// Bit 0 selects shift, bit 1 ctrl, bit 2 alt, bit 3 meta: all 15 non-empty combinations of
470508
// single modifiers, pairs, triples, and the full quad.
471509
for (unsigned combination = 1; combination < (1u << modifiers.size()); ++combination) {
472-
// Each held key is reported with the modifiers accumulated so far, matching a client
473-
// that presses them one at a time.
474-
std::vector<std::pair<std::uint16_t, std::uint8_t>> held;
475-
std::uint8_t claimed = 0;
476-
std::string label;
477-
for (std::size_t index = 0; index < modifiers.size(); ++index) {
478-
if (!(combination & (1u << index))) {
479-
continue;
480-
}
481-
claimed |= modifiers[index].bit;
482-
held.emplace_back(key_for_side(modifiers[index], side), claimed);
483-
label += (label.empty() ? "" : "+");
484-
label += modifiers[index].name;
485-
}
486-
SCOPED_TRACE(label + " (side " + std::to_string(static_cast<int>(side)) + ")");
510+
const auto held = select_modifiers(combination, side);
511+
SCOPED_TRACE(std::format("{} (side {})", held.label, static_cast<int>(side)));
487512

488513
std::vector<std::string> expected;
489-
for (const auto &[key_code, reported] : held) {
514+
for (const auto &[key_code, reported] : held.keys) {
490515
press(key_code, reported);
491516
expected.push_back(pressed(key_code));
492517
}
493518

494-
press(VKEY_A, claimed);
495-
release(VKEY_A, claimed);
519+
press(VKEY_A, held.claimed);
520+
release(VKEY_A, held.claimed);
496521
expected.push_back(pressed(VKEY_A));
497522
expected.push_back(released(VKEY_A));
498523

499-
for (auto key = held.rbegin(); key != held.rend(); ++key) {
500-
release(key->first, claimed);
524+
for (auto key = held.keys.rbegin(); key != held.keys.rend(); ++key) {
525+
release(key->first, held.claimed);
501526
expected.push_back(released(key->first));
502527
}
503528

@@ -510,7 +535,7 @@ TEST_F(KeyboardPassthroughTest, InjectsSyntheticModifiersTheClientHoldsWithoutSe
510535
// Moonlight can report a modifier without ever sending its key event, for example when the
511536
// client's own compositor swallowed it. Sunshine wraps the key in a real modifier press.
512537
struct injectable_t {
513-
std::uint8_t bit;
538+
unsigned bit;
514539
std::uint16_t key_code;
515540
};
516541

@@ -522,7 +547,7 @@ TEST_F(KeyboardPassthroughTest, InjectsSyntheticModifiersTheClientHoldsWithoutSe
522547

523548
for (unsigned combination = 1; combination < (1u << injectable.size()); ++combination) {
524549
std::vector<std::string> expected;
525-
std::uint8_t claimed = 0;
550+
unsigned claimed = 0;
526551
for (std::size_t index = 0; index < injectable.size(); ++index) {
527552
if (combination & (1u << index)) {
528553
claimed |= injectable[index].bit;
@@ -799,9 +824,9 @@ TEST_F(KeyboardPassthroughTest, ReleasesHeldKeysAsThemselvesWithoutKeybindings)
799824

800825
// key_press is an unordered_map, so the release order is unspecified.
801826
auto released_keys = taken();
802-
std::sort(released_keys.begin(), released_keys.end());
803-
std::vector<std::string> expected {released(VKEY_LMENU), released(VKEY_A)};
804-
std::sort(expected.begin(), expected.end());
827+
std::ranges::sort(released_keys);
828+
std::vector expected {released(VKEY_LMENU), released(VKEY_A)};
829+
std::ranges::sort(expected);
805830
EXPECT_EQ(released_keys, expected);
806831
}
807832

0 commit comments

Comments
 (0)