Repository navigation
Groundwork: mutation pass over the carried modules - #171
Merged
Merged
Conversation
One cargo-mutants 27.1.0 pass over obsbot-device (without the 0.13-only state and pose modules) left 202 missed mutants. These tests kill the ones that sit in code without I/O or behind the scripted device: frame length limits and the rejection layout, the pair gap, the sequence counter, builder limits and the sign of a zero rate, preset and status decoders, the Camera reads and checks, and how the device handles a refused ioctl. The scripted device also records set_ctrl calls and can give a control its own range; its doc comment now describes only what the test double does. The plan names the tilt negation by its function tilt_to_pitch and says how the run treats cfg(test) code. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The scripted device sits behind five methods since set_ctrl was added; the module doc said four. The plan now lists the open call-site item of tilt_to_pitch under "After the tasks". Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #170
One cargo-mutants pass over the carried modules (plan Task 8). It found 202 missed mutants; 100 of them are now killed by new tests, 102 are recorded below with one disposition each. No production code changed (only
#[cfg(test)]code and the plan text), and no fault in the carried-over code turned up.Run record
cargo-mutants 27.1.0.56731ce(rebuild). Final tree of this pull request: see the head commit.builders_emit_the_recorded_bytes) is skipped on purpose: it records what the builders emit, so it would kill builder mutants that no real test catches.66287b5plus 3 infamily/exchange.rsand 4 inmodels/tiny2lite/camera.rsfrom Answers: test the poll in answer (rejected, empty, no answer, read error) #151 and Gimbal: the tilt negation is untested; make it a pure function with a test #153).56731ce:854 mutants tested in 17m: 202 missed, 585 caught, 65 unviable, 2 timeouts.854 mutants tested in 26m: 102 missed, 685 caught, 65 unviable, 2 timeouts.git statusprinted nothing after each run.lock_exclusive: the guardInstant::now() < deadlinereplaced bytrue;free_view_id:delete !). They count as detected, not as missed.transport/uvc.rsused the same temporary file name. The name now has a counter; the library tests ran 20 times in a row on the final tree without a failure, and the run above is the one on the fixed tree.cargo mutantslists no mutant inside#[cfg(test)]items, soscripted.rsandDevice::scriptedare neither mutated nor counted. The#[cfg(test)]hooks insideDevice::xu_get,xu_set,get_ctrl,set_ctrlandctrl_rangeare part of those production functions; a mutant that replaces such a function replaces its hook too, and the tests then run against it like against any other.Dispositions
forget_gimbalmissed.txtNo D or L mutant is in a function without I/O. D by place:
-> Ok(()); nine of them,set_ai_mode,zone_view_go,set_exposure_mode,factory_reset,gimbal,gimbal_rate,gimbal_stop,gimbal_centerandrecall_preset, also end in a write of the remembered state, but dropping the whole body drops the frames too, so the replay transport of Track A covers them) or a read of the preset list (selectors 0x0c / 0x0d, which the scripted device does not serve)card_name,detect: ioctl on a/dev/video*node, a read of/dev)is_streaming,probe,apps_using: REQBUFS ioctl, a read of the real/proc)Device::ctrl_range: the QUERYCTRL result of a real device)The K tests use the existing scripted device (
Cameraflows without a camera) where a read or a check is behind a method, and call pure functions directly. Most D mutants areCamera::set_*methods that replace the whole body withOk(()); the scripted device drops the frames they write, so only a replay transport can show that the frame went out. That is Track A's job and is not started here.Equivalent mutants:
models/tiny2lite/camera.rs:570:36: replace + with - in Camera::live_pose_and_zoom:x + 0.0andx - 0.0differ only forx == -0.0, and the tilt comes fromLivePose::decode, which already adds0.0(killed there by a test).models/tiny2lite/commands.rs:198:28: replace | with ^ in default_work_mode:main << 4has the low nibble clear and everyAiModesub-mode is 0-4, so|and^give the same byte.models/tiny2lite/presets.rs:407:32: replace | with ^ in decode_base64:buffer << 6has its low six bits clear and a base64 value is below 64, so|and^give the same word.transport/uvc.rs:91:60: replace < with <= in lock_exclusive:<against<=differs only whenInstant::now()equals the deadline exactly, which no input controls; the effect would be one more 20 ms retry.Named mutants (spec 10.2)
delete match arm 0x89 in parse_answer(family/frame.rs:120): caught.delete -in the angle conversions is caught:LivePose::decode(1),HandArea::decode(2),tilt_to_pitch_range(2),pitch_to_tilt_range(2), andtilt_to_pitch(1), which is where the gimbal's tilt negation lives since Gimbal: the tilt negation is untested; make it a pure function with a test #153 (the plan'sCamera::gimbalpattern is corrected in the plan document).Answer::Rejectedarm ofpoll_answerchanged to=> {}):a_rejected_answer_returns_the_cameras_code_at_the_first_pollfails (test result: FAILED. 170 passed; 1 failed). Reverted;git statusclean.| Error::Rejected(..)removed from the tolerated errors offull_status_with):a_rejected_tracking_query_gives_tracking_null_and_the_rest_of_the_statusfails (170 passed; 1 failed). Reverted;git statusclean.tilt_to_pitchinCamera::gimbal. A hand-made mutantlet tilt_wire = tilt;leaves all 171 library tests green, becauseCamera::gimbalends in a write of the remembered state and no test calls it. The negation itself is covered (delete - in tilt_to_pitchis caught andtilt_to_pitchhas its own tests); the wiring needs a seam for the state path or the replay transport. Listed as an open item under "After the tasks" in the plan document (changed in this pull request); the coordinator may also open an issue for it.replace Camera::gimbal -> Result<()> with Ok(())is in the table below as L.Changes
family/frame.rs,family/exchange.rs,models/tiny2lite/{commands,presets,status}.rs,transport/uvc.rsandlegacy/full_status.rs, and a new modulescripted_camera_testsinmodels/tiny2lite/camera.rs(the same pattern as inlegacy/full_status.rs). 42 new test names, none removed or renamed.scripted.rs(test-only): the doc comment ofScriptednow says only what the double does (it answers an unknown query with type 0x89, code -1, so a test does not wait out the poll; no statement about the camera). New: the table recordsset_ctrlcalls, a control can have its own range, andcamera(script)builds aCameraon it.transport/uvc.rs: four lines under#[cfg(test)]inDevice::set_ctrl(the same hook the other methods have). The production build is unchanged.tilt_to_pitch, the expected result of Step 5 and the M disposition say what is expected now that the seams of P3 are onrebuild, and Step 2 says how the run treats#[cfg(test)]code. The P5 sentence about the shellcheck gate already says it covers every script (commit56731ce).an_entry_reads_each_pose_word_from_its_own_bytes(offsets ofparse_entry) andone_byte_past_the_data_section_is_refused(the 44-byte limit of the frame parser).Verification (no camera)
cargo fmt --check,cargo clippy --workspace --all-targets --locked -- -D warnings,shellcheck scripts/*.sh scripts/groundwork/*.sh,actionlint .github/workflows/*.yml: no output.cargo test --workspace --locked: 171 library tests,byte_guard1,cli_surface2 and the others pass; the library tests were run 20 times in a row without a failure.test-names.shlists 157 names on the base and 199 on the head;comm -23(lost) prints nothing,comm -13prints the 42 new names.tests/byte_guard.rs,tests/cli_surface.rsand their fixtures are not touched and pass.move-check.sh 56731ce HEADprints 649 added lines and 4 removed ones: the two doc-comment lines and the two default-range lines of the scripted device. All are#[cfg(test)]code; no line of the production build changed.One caveat for the reader:
two_offset_frames_are_a_gap_apart_and_one_frame_needs_no_gapmeasures time. The lower bound (a pair takes at least the 100 ms gap) cannot flake; the upper bound (a single frame returns in under 100 ms) has a margin of several orders of magnitude over a scripted call, but a stalled machine could still trip it.Missed mutants with their disposition (102)
enumeration.rs:39:5: replace card_name -> Option<(String, u32)> with None— Denumeration.rs:39:5: replace card_name -> Option<(String, u32)> with Some((String::new(), 0))— Denumeration.rs:39:5: replace card_name -> Option<(String, u32)> with Some((String::new(), 1))— Denumeration.rs:39:5: replace card_name -> Option<(String, u32)> with Some(("xyzzy".into(), 0))— Denumeration.rs:39:5: replace card_name -> Option<(String, u32)> with Some(("xyzzy".into(), 1))— Denumeration.rs:44:26: replace == with != in card_name— Denumeration.rs:55:5: replace detect -> Option<Detected> with None— Denumeration.rs:78:21: replace & with | in detect— Denumeration.rs:78:21: replace & with ^ in detect— Denumeration.rs:78:45: replace == with != in detect— Dfamily/exchange.rs:40:9: replace Camera::short -> Result<()> with Ok(())— Dfamily/streaming.rs:63:5: replace is_streaming -> bool with false— Dfamily/streaming.rs:76:9: delete field type_ from struct V4l2RequestBuffers expression in probe— Dfamily/streaming.rs:77:9: delete field memory from struct V4l2RequestBuffers expression in probe— Dfamily/streaming.rs:113:5: replace apps_using -> Vec<String> with vec![]— Dfamily/streaming.rs:113:5: replace apps_using -> Vec<String> with vec![String::new()]— Dfamily/streaming.rs:113:5: replace apps_using -> Vec<String> with vec!["xyzzy".into()]— Dmodels/tiny2lite/camera.rs:424:9: replace Camera::sleep -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:427:9: replace Camera::set_ai_mode -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:428:17: replace != with == in Camera::set_ai_mode— Lmodels/tiny2lite/camera.rs:431:17: delete field gimbal from struct State expression in Camera::set_ai_mode— Lmodels/tiny2lite/camera.rs:439:9: replace Camera::set_tracking_speed -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:442:9: replace Camera::set_speed_mode -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:445:9: replace Camera::set_auto_zoom -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:448:9: replace Camera::set_framing_size -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:451:9: replace Camera::set_auto_zoom_speed -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:454:9: replace Camera::set_part_mode -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:478:9: replace Camera::set_hdr -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:481:9: replace Camera::set_auto_sleep -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:491:9: replace Camera::set_sleep_background -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:494:9: replace Camera::set_sleep_background_mirror -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:497:9: replace Camera::set_led -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:500:9: replace Camera::set_led_blink -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:510:9: replace Camera::set_default_work_mode -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:513:9: replace Camera::set_field_of_view -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:516:9: replace Camera::set_mirror -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:519:9: replace Camera::set_invert -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:522:9: replace Camera::set_mic_in_sleep -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:525:9: replace Camera::set_noise_reduction -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:528:9: replace Camera::set_auto_gain -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:533:9: replace Camera::set_audio_volume -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:536:9: replace Camera::set_pickup_distance -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:539:9: replace Camera::set_microphone -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:542:9: replace Camera::set_gesture -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:545:9: replace Camera::set_gesture_zoom_ratio -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:548:9: replace Camera::set_hand_side -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:551:9: replace Camera::set_zone_tracking -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:557:9: replace Camera::set_zone_area -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:570:36: replace + with - in Camera::live_pose_and_zoom— Emodels/tiny2lite/camera.rs:592:9: replace Camera::set_hand_area -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:601:9: replace Camera::hand_area_reset -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:609:9: replace Camera::set_hand_pan -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:613:9: replace Camera::set_hand_tilt -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:617:9: replace Camera::set_hand_return_after -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:622:9: replace Camera::set_hand_gimbal -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:647:9: replace Camera::rename_zone_view -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:651:9: replace Camera::delete_zone_view -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:657:9: replace Camera::zone_view_go -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:659:13: delete field gimbal from struct State expression in Camera::zone_view_go— Lmodels/tiny2lite/camera.rs:667:9: replace Camera::hand_return_go -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:672:9: replace Camera::hand_return_reset -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:677:9: replace Camera::zone_return_go -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:682:9: replace Camera::startup_go -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:686:9: replace Camera::set_zone_follow -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:691:9: replace Camera::set_autofocus_mode -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:696:9: replace Camera::set_autofocus_select -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:702:9: replace Camera::focus_point -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:733:9: replace Camera::set_startup -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:747:9: replace Camera::set_startup_ai -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:756:61: replace == with != in Camera::set_startup_preset— Dmodels/tiny2lite/camera.rs:775:9: replace Camera::set_exposure_mode -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:778:13: delete field exposure_mode from struct State expression in Camera::set_exposure_mode— Lmodels/tiny2lite/camera.rs:801:9: replace Camera::factory_reset -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:813:9: replace Camera::gimbal -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:829:9: replace Camera::gimbal_rate -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:838:9: replace Camera::gimbal_stop -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:845:9: replace Camera::gimbal_center -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:853:9: replace Camera::forget_gimbal -> Result<()> with Ok(())— Lmodels/tiny2lite/camera.rs:854:13: delete field gimbal from struct State expression in Camera::forget_gimbal— Lmodels/tiny2lite/camera.rs:862:9: replace Camera::zoom_to -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:867:9: replace Camera::zoom_stop -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:922:9: replace Camera::presets -> Result<Vec<Preset>> with Ok(vec![])— Dmodels/tiny2lite/camera.rs:948:60: replace == with != in Camera::save_preset— Dmodels/tiny2lite/camera.rs:956:38: delete ! in Camera::save_preset— Dmodels/tiny2lite/camera.rs:964:9: replace Camera::rename_preset -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:968:9: replace Camera::delete_preset -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:973:9: replace Camera::recall_preset -> Result<()> with Ok(())— Dmodels/tiny2lite/camera.rs:974:50: replace == with != in Camera::recall_preset— Lmodels/tiny2lite/camera.rs:975:66: replace == with != in Camera::recall_preset— Lmodels/tiny2lite/camera.rs:977:24: replace match guard static_mode with true in Camera::recall_preset— Lmodels/tiny2lite/camera.rs:977:24: replace match guard static_mode with false in Camera::recall_preset— Lmodels/tiny2lite/camera.rs:978:17: delete field gimbal from struct State expression in Camera::recall_preset— Lmodels/tiny2lite/camera.rs:984:17: delete field gimbal from struct State expression in Camera::recall_preset— Lmodels/tiny2lite/commands.rs:198:28: replace | with ^ in default_work_mode— Emodels/tiny2lite/presets.rs:407:32: replace | with ^ in decode_base64— Etransport/uvc.rs:91:60: replace < with <= in lock_exclusive— Etransport/uvc.rs:225:13: delete field id from struct V4l2QueryCtrl expression in Device::ctrl_range— Dtransport/uvc.rs:229:22: replace match guard q.flags & V4L2_CTRL_FLAG_DISABLED == 0 with true in Device::ctrl_range— Dtransport/uvc.rs:229:22: replace match guard q.flags & V4L2_CTRL_FLAG_DISABLED == 0 with false in Device::ctrl_range— Dtransport/uvc.rs:229:30: replace & with | in Device::ctrl_range— Dtransport/uvc.rs:229:30: replace & with ^ in Device::ctrl_range— Dtransport/uvc.rs:229:56: replace == with != in Device::ctrl_range— DKilled mutants and the test that fails on each (100)
family/exchange.rs:22:5: replace sequence -> &'static Sequence with Box::leak(Box::new(Default::default()))→all_cameras_of_a_process_share_one_sequencefamily/exchange.rs:48:9: replace Camera::pair -> Result<()> with Ok(())→a_pair_fails_when_either_frame_cannot_be_sent,a_pair_is_sent_with_a_gap_between_its_framesfamily/frame.rs:13:40: replace - with +→a_frame_holds_exactly_44_bytes_of_datafamily/frame.rs:101:30: replace && with || in error_code→a_rejection_needs_a_16_byte_header_and_the_extension_tagfamily/frame.rs:113:20: replace < with == in parse_answer→a_reply_must_hold_both_headers_to_be_an_answerfamily/frame.rs:113:20: replace < with <= in parse_answer→a_reply_must_hold_both_headers_to_be_an_answerfamily/frame.rs:113:34: replace || with && in parse_answer→a_reply_must_hold_both_headers_to_be_an_answerlegacy/full_status.rs:202:40: replace == with != in Camera::full_status_with→the_target_is_asked_only_while_ai_tracking_is_onmodels/tiny2lite/camera.rs:67:48: replace + with - in LivePose::decode→a_live_pose_at_pitch_zero_has_a_positive_zero_tiltmodels/tiny2lite/camera.rs:132:34: replace + with - in HandArea::decode→hand_area_pitch_limits_of_zero_read_as_positive_zero_tiltsmodels/tiny2lite/camera.rs:133:34: replace + with - in HandArea::decode→hand_area_pitch_limits_of_zero_read_as_positive_zero_tiltsmodels/tiny2lite/camera.rs:160:31: replace + with - in pitch_to_tilt_range→a_zero_pitch_minimum_reads_as_a_positive_zero_tilt_maximummodels/tiny2lite/camera.rs:265:9: replace Camera::target -> Result<Option<Target>> with Ok(None)→the_target_is_asked_only_while_ai_tracking_is_on,the_tracking_target_is_read_from_query_0x01c4models/tiny2lite/camera.rs:279:9: replace Camera::image_controls -> Result<BTreeMap<String, ImageControl>> with Ok(BTreeMap::new())→image_controls_lists_the_controls_the_camera_has_with_their_valuesmodels/tiny2lite/camera.rs:296:9: replace Camera::query_f32 -> Result<f32> with Ok(0.0)→a_hand_return_time_is_zero_or_more_else_it_is_not_shown,the_framing_options_are_read_from_their_queries,the_hand_tracking_is_read_from_its_queries(+1)models/tiny2lite/camera.rs:296:9: replace Camera::query_f32 -> Result<f32> with Ok(1.0)→a_hand_return_time_is_zero_or_more_else_it_is_not_shown,the_framing_options_are_read_from_their_queries,the_hand_tracking_is_read_from_its_queries(+1)models/tiny2lite/camera.rs:296:9: replace Camera::query_f32 -> Result<f32> with Ok(-1.0)→a_hand_return_time_is_zero_or_more_else_it_is_not_shown,the_framing_options_are_read_from_their_queries,the_hand_tracking_is_read_from_its_queries(+1)models/tiny2lite/camera.rs:320:68: replace != with == in Camera::zone→the_zone_is_read_from_its_queries_in_obsbot4linux_anglesmodels/tiny2lite/camera.rs:339:69: replace >= with < in Camera::hand→a_hand_return_time_is_zero_or_more_else_it_is_not_shown,the_hand_tracking_is_read_from_its_queriesmodels/tiny2lite/camera.rs:347:9: replace Camera::zone_views -> Result<Vec<ZoneView>> with Ok(vec![])→the_zone_is_read_from_its_queries_in_obsbot4linux_anglesmodels/tiny2lite/camera.rs:397:9: replace Camera::query_u8 -> Result<u8> with Ok(0)→the_framing_options_are_read_from_their_queries,the_initial_state_ai_byte_is_the_first_byte_of_0x3e84models/tiny2lite/camera.rs:397:9: replace Camera::query_u8 -> Result<u8> with Ok(1)→the_framing_options_are_read_from_their_queries,the_initial_state_ai_byte_is_the_first_byte_of_0x3e84models/tiny2lite/camera.rs:410:38: replace != with == in Camera::framing→the_framing_options_are_read_from_their_queriesmodels/tiny2lite/camera.rs:458:9: replace Camera::set_framing_offset -> Result<()> with Ok(())→the_framing_offset_needs_an_axis,two_offset_frames_are_a_gap_apart_and_one_frame_needs_no_gapmodels/tiny2lite/camera.rs:458:24: replace && with || in Camera::set_framing_offset→the_framing_offset_needs_an_axis,two_offset_frames_are_a_gap_apart_and_one_frame_needs_no_gapmodels/tiny2lite/camera.rs:469:16: delete ! in Camera::set_framing_offset→two_offset_frames_are_a_gap_apart_and_one_frame_needs_no_gapmodels/tiny2lite/camera.rs:504:9: replace Camera::set_wake_zoom -> Result<u8> with Ok(0)→the_wake_up_zoom_is_reported_as_the_percent_that_was_storedmodels/tiny2lite/camera.rs:504:9: replace Camera::set_wake_zoom -> Result<u8> with Ok(1)→the_wake_up_zoom_is_reported_as_the_percent_that_was_storedmodels/tiny2lite/camera.rs:570:36: replace + with * in Camera::live_pose_and_zoom→the_current_pose_is_the_live_pose_and_the_zoom_with_the_zoom_kept_in_rangemodels/tiny2lite/camera.rs:707:9: replace Camera::set_autofocus -> Result<()> with Ok(())→autofocus_is_the_continuous_autofocus_controlmodels/tiny2lite/camera.rs:712:9: replace Camera::set_focus -> Result<()> with Ok(())→manual_focus_turns_autofocus_off_and_stays_in_rangemodels/tiny2lite/camera.rs:716:18: replace < with == in Camera::set_focus→manual_focus_turns_autofocus_off_and_stays_in_rangemodels/tiny2lite/camera.rs:716:18: replace < with > in Camera::set_focus→manual_focus_turns_autofocus_off_and_stays_in_rangemodels/tiny2lite/camera.rs:716:18: replace < with <= in Camera::set_focus→manual_focus_turns_autofocus_off_and_stays_in_rangemodels/tiny2lite/camera.rs:716:26: replace || with && in Camera::set_focus→manual_focus_turns_autofocus_off_and_stays_in_rangemodels/tiny2lite/camera.rs:716:35: replace > with == in Camera::set_focus→manual_focus_turns_autofocus_off_and_stays_in_rangemodels/tiny2lite/camera.rs:716:35: replace > with < in Camera::set_focus→manual_focus_turns_autofocus_off_and_stays_in_rangemodels/tiny2lite/camera.rs:716:35: replace > with >= in Camera::set_focus→manual_focus_turns_autofocus_off_and_stays_in_rangemodels/tiny2lite/camera.rs:725:9: replace Camera::startup_ai_byte -> Result<u8> with Ok(0)→the_initial_state_ai_byte_is_the_first_byte_of_0x3e84models/tiny2lite/camera.rs:725:9: replace Camera::startup_ai_byte -> Result<u8> with Ok(1)→the_initial_state_ai_byte_is_the_first_byte_of_0x3e84models/tiny2lite/camera.rs:791:9: replace Camera::set_manual_exposure -> Result<()> with Ok(())→manual_exposure_switches_first_and_then_sets_time_and_gainmodels/tiny2lite/camera.rs:872:9: replace Camera::set_zoom -> Result<()> with Ok(())→zoom_maps_the_factor_onto_the_range_of_the_zoom_controlmodels/tiny2lite/camera.rs:872:12: delete ! in Camera::set_zoom→zoom_maps_the_factor_onto_the_range_of_the_zoom_controlmodels/tiny2lite/camera.rs:879:29: replace - with + in Camera::set_zoom→zoom_maps_the_factor_onto_the_range_of_the_zoom_controlmodels/tiny2lite/camera.rs:879:29: replace - with / in Camera::set_zoom→zoom_maps_the_factor_onto_the_range_of_the_zoom_controlmodels/tiny2lite/camera.rs:879:36: replace / with % in Camera::set_zoom→zoom_maps_the_factor_onto_the_range_of_the_zoom_controlmodels/tiny2lite/camera.rs:879:36: replace / with * in Camera::set_zoom→zoom_maps_the_factor_onto_the_range_of_the_zoom_controlmodels/tiny2lite/camera.rs:879:48: replace - with + in Camera::set_zoom→zoom_maps_the_factor_onto_the_range_of_the_zoom_controlmodels/tiny2lite/camera.rs:879:48: replace - with / in Camera::set_zoom→zoom_maps_the_factor_onto_the_range_of_the_zoom_controlmodels/tiny2lite/camera.rs:880:27: replace + with - in Camera::set_zoom→zoom_maps_the_factor_onto_the_range_of_the_zoom_controlmodels/tiny2lite/camera.rs:880:27: replace + with * in Camera::set_zoom→zoom_maps_the_factor_onto_the_range_of_the_zoom_controlmodels/tiny2lite/camera.rs:880:36: replace * with + in Camera::set_zoom→zoom_maps_the_factor_onto_the_range_of_the_zoom_controlmodels/tiny2lite/camera.rs:880:36: replace * with / in Camera::set_zoom→zoom_maps_the_factor_onto_the_range_of_the_zoom_controlmodels/tiny2lite/camera.rs:880:45: replace - with + in Camera::set_zoom→zoom_maps_the_factor_onto_the_range_of_the_zoom_controlmodels/tiny2lite/camera.rs:880:45: replace - with / in Camera::set_zoom→zoom_maps_the_factor_onto_the_range_of_the_zoom_controlmodels/tiny2lite/camera.rs:887:9: replace Camera::image_id -> Result<u32> with Ok(0)→a_shutter_time_needs_manual_exposure,an_image_value_must_be_inside_the_range_of_the_control,image_names_map_to_their_v4l2_ids(+1)models/tiny2lite/camera.rs:887:9: replace Camera::image_id -> Result<u32> with Ok(1)→a_shutter_time_needs_manual_exposure,an_image_value_must_be_inside_the_range_of_the_control,image_names_map_to_their_v4l2_ids(+1)models/tiny2lite/camera.rs:889:31: replace == with != in Camera::image_id→a_shutter_time_needs_manual_exposure,an_image_value_must_be_inside_the_range_of_the_control,image_names_map_to_their_v4l2_ids(+1)models/tiny2lite/camera.rs:896:9: replace Camera::image_id_in_range -> Result<u32> with Ok(0)→a_shutter_time_needs_manual_exposure,an_image_value_must_be_inside_the_range_of_the_control,manual_exposure_switches_first_and_then_sets_time_and_gainmodels/tiny2lite/camera.rs:896:9: replace Camera::image_id_in_range -> Result<u32> with Ok(1)→a_shutter_time_needs_manual_exposure,an_image_value_must_be_inside_the_range_of_the_control,manual_exposure_switches_first_and_then_sets_time_and_gainmodels/tiny2lite/camera.rs:901:18: replace < with == in Camera::image_id_in_range→an_image_value_must_be_inside_the_range_of_the_controlmodels/tiny2lite/camera.rs:901:18: replace < with > in Camera::image_id_in_range→a_shutter_time_needs_manual_exposure,an_image_value_must_be_inside_the_range_of_the_control,manual_exposure_switches_first_and_then_sets_time_and_gainmodels/tiny2lite/camera.rs:901:18: replace < with <= in Camera::image_id_in_range→an_image_value_must_be_inside_the_range_of_the_controlmodels/tiny2lite/camera.rs:901:26: replace || with && in Camera::image_id_in_range→an_image_value_must_be_inside_the_range_of_the_controlmodels/tiny2lite/camera.rs:901:35: replace > with == in Camera::image_id_in_range→an_image_value_must_be_inside_the_range_of_the_control,manual_exposure_switches_first_and_then_sets_time_and_gainmodels/tiny2lite/camera.rs:901:35: replace > with < in Camera::image_id_in_range→a_shutter_time_needs_manual_exposure,an_image_value_must_be_inside_the_range_of_the_control,manual_exposure_switches_first_and_then_sets_time_and_gainmodels/tiny2lite/camera.rs:901:35: replace > with >= in Camera::image_id_in_range→an_image_value_must_be_inside_the_range_of_the_control,manual_exposure_switches_first_and_then_sets_time_and_gainmodels/tiny2lite/camera.rs:908:9: replace Camera::set_image -> Result<()> with Ok(())→a_shutter_time_needs_manual_exposure,manual_exposure_switches_first_and_then_sets_time_and_gainmodels/tiny2lite/camera.rs:910:17: replace == with != in Camera::set_image→a_shutter_time_needs_manual_exposuremodels/tiny2lite/camera.rs:910:36: replace && with || in Camera::set_image→a_shutter_time_needs_manual_exposure,manual_exposure_switches_first_and_then_sets_time_and_gainmodels/tiny2lite/camera.rs:910:73: replace != with == in Camera::set_image→a_shutter_time_needs_manual_exposure,manual_exposure_switches_first_and_then_sets_time_and_gainmodels/tiny2lite/commands.rs:170:19: replace > with >= in led→a_documented_limit_is_accepted_and_one_past_it_is_notmodels/tiny2lite/commands.rs:182:16: replace > with >= in wake_zoom→a_documented_limit_is_accepted_and_one_past_it_is_not,the_wake_up_zoom_is_reported_as_the_percent_that_was_storedmodels/tiny2lite/commands.rs:312:48: replace < with <= in area→an_area_needs_its_minimum_below_its_maximummodels/tiny2lite/commands.rs:347:40: replace < with <= in hand_return_after→a_documented_limit_is_accepted_and_one_past_it_is_notmodels/tiny2lite/commands.rs:383:15: replace > with >= in audio_volume→a_documented_limit_is_accepted_and_one_past_it_is_notmodels/tiny2lite/commands.rs:495:57: replace + with - in gimbal_rate→a_rate_of_zero_sends_twelve_zero_bytes_whatever_mirror_and_invert_aremodels/tiny2lite/commands.rs:499:28: replace + with - in gimbal_rate→a_rate_of_zero_sends_twelve_zero_bytes_whatever_mirror_and_invert_aremodels/tiny2lite/commands.rs:525:14: replace < with <= in zoom_to→a_documented_limit_is_accepted_and_one_past_it_is_notmodels/tiny2lite/presets.rs:288:9: delete match arm 2 in startup_ai_mode→startup_ai_mode_names_the_documented_bytesmodels/tiny2lite/presets.rs:290:9: delete match arm 4 in startup_ai_mode→startup_ai_mode_names_the_documented_bytesmodels/tiny2lite/presets.rs:340:20: replace < with <= in StartupActions::decode→startup_actions_need_the_24_bytes_up_to_the_huemodels/tiny2lite/presets.rs:367:24: replace < with <= in parse_slots→a_slot_index_outside_the_slots_is_not_listedmodels/tiny2lite/presets.rs:389:34: replace + with - in parse_entry→a_preset_at_tilt_zero_reads_as_positive_zeromodels/tiny2lite/presets.rs:399:9: delete match arm b'0'..= b'9' in decode_base64→base64_names_use_the_standard_alphabetmodels/tiny2lite/presets.rs:399:31: replace - with + in decode_base64→base64_names_use_the_standard_alphabetmodels/tiny2lite/presets.rs:399:31: replace - with / in decode_base64→base64_names_use_the_standard_alphabetmodels/tiny2lite/presets.rs:399:38: replace + with - in decode_base64→base64_names_use_the_standard_alphabetmodels/tiny2lite/presets.rs:400:9: delete match arm b'+' in decode_base64→base64_names_use_the_standard_alphabetmodels/tiny2lite/presets.rs:401:9: delete match arm b'/' in decode_base64→base64_names_use_the_standard_alphabetmodels/tiny2lite/status.rs:28:13: delete match arm 0 in FovReading::from_u8→field_of_view_reading_names_every_value_the_camera_reportsmodels/tiny2lite/status.rs:54:13: delete match arm 0 in AntiFlicker::from_u8→anti_flicker_names_every_value_of_power_line_frequencytransport/uvc.rs:154:9: replace Device::xu -> Result<()> with Ok(())→a_payload_longer_than_the_buffer_is_refused_before_any_ioctl,a_refused_ioctl_is_an_error_not_a_default_valuetransport/uvc.rs:166:9: replace Device::xu_len -> Result<usize> with Ok(0)→a_payload_longer_than_the_buffer_is_refused_before_any_ioctl,a_refused_ioctl_is_an_error_not_a_default_valuetransport/uvc.rs:166:9: replace Device::xu_len -> Result<usize> with Ok(1)→a_payload_longer_than_the_buffer_is_refused_before_any_ioctl,a_refused_ioctl_is_an_error_not_a_default_valuetransport/uvc.rs:189:26: replace > with == in Device::xu_set→a_payload_longer_than_the_buffer_is_refused_before_any_ioctltransport/uvc.rs:189:26: replace > with < in Device::xu_set→a_payload_longer_than_the_buffer_is_refused_before_any_ioctltransport/uvc.rs:189:26: replace > with >= in Device::xu_set→a_payload_longer_than_the_buffer_is_refused_before_any_ioctltransport/uvc.rs:209:9: replace Device::set_ctrl -> Result<()> with Ok(())→a_refused_ioctl_is_an_error_not_a_default_value,a_shutter_time_needs_manual_exposure,autofocus_is_the_continuous_autofocus_control(+3)transport/uvc.rs:220:9: replace Device::ctrl_range -> Result<Option<CtrlRange>> with Ok(None)→a_refused_ioctl_is_an_error_not_a_default_value,a_shutter_time_needs_manual_exposure,an_image_value_must_be_inside_the_range_of_the_control(+4)New test names (42)
angle_conversion_tests::a_live_pose_at_pitch_zero_has_a_positive_zero_tiltangle_conversion_tests::a_zero_pitch_minimum_reads_as_a_positive_zero_tilt_maximumangle_conversion_tests::hand_area_pitch_limits_of_zero_read_as_positive_zero_tiltsscripted_camera_tests::a_hand_return_time_is_zero_or_more_else_it_is_not_shownscripted_camera_tests::a_shutter_time_needs_manual_exposurescripted_camera_tests::an_image_value_must_be_inside_the_range_of_the_controlscripted_camera_tests::autofocus_is_the_continuous_autofocus_controlscripted_camera_tests::image_controls_lists_the_controls_the_camera_has_with_their_valuesscripted_camera_tests::image_names_map_to_their_v4l2_idsscripted_camera_tests::manual_exposure_switches_first_and_then_sets_time_and_gainscripted_camera_tests::manual_focus_turns_autofocus_off_and_stays_in_rangescripted_camera_tests::the_current_pose_is_the_live_pose_and_the_zoom_with_the_zoom_kept_in_rangescripted_camera_tests::the_framing_offset_needs_an_axisscripted_camera_tests::the_framing_options_are_read_from_their_queriesscripted_camera_tests::the_hand_tracking_is_read_from_its_queriesscripted_camera_tests::the_initial_state_ai_byte_is_the_first_byte_of_0x3e84scripted_camera_tests::the_target_is_asked_only_while_ai_tracking_is_onscripted_camera_tests::the_tracking_target_is_read_from_query_0x01c4scripted_camera_tests::the_wake_up_zoom_is_reported_as_the_percent_that_was_storedscripted_camera_tests::the_zone_is_read_from_its_queries_in_obsbot4linux_anglesscripted_camera_tests::two_offset_frames_are_a_gap_apart_and_one_frame_needs_no_gapscripted_camera_tests::zoom_maps_the_factor_onto_the_range_of_the_zoom_controltests::a_documented_limit_is_accepted_and_one_past_it_is_nottests::a_frame_holds_exactly_44_bytes_of_datatests::a_pair_fails_when_either_frame_cannot_be_senttests::a_pair_is_sent_with_a_gap_between_its_framestests::a_payload_longer_than_the_buffer_is_refused_before_any_ioctltests::a_preset_at_tilt_zero_reads_as_positive_zerotests::a_rate_of_zero_sends_twelve_zero_bytes_whatever_mirror_and_invert_aretests::a_refused_ioctl_is_an_error_not_a_default_valuetests::a_rejection_needs_a_16_byte_header_and_the_extension_tagtests::a_reply_must_hold_both_headers_to_be_an_answertests::a_slot_index_outside_the_slots_is_not_listedtests::all_cameras_of_a_process_share_one_sequencetests::an_area_needs_its_minimum_below_its_maximumtests::an_entry_reads_each_pose_word_from_its_own_bytestests::anti_flicker_names_every_value_of_power_line_frequencytests::base64_names_use_the_standard_alphabettests::field_of_view_reading_names_every_value_the_camera_reportstests::one_byte_past_the_data_section_is_refusedtests::startup_actions_need_the_24_bytes_up_to_the_huetests::startup_ai_mode_names_the_documented_bytes🤖 Generated with Claude Code