Skip to content

Model the fake voice recorder on a cancellable recording job - #7466

Open
hayaksi1 wants to merge 2 commits into
element-hq:developfrom
hayaksi1:fix/4770-fake-voice-recorder
Open

Model the fake voice recorder on a cancellable recording job#7466
hayaksi1 wants to merge 2 commits into
element-hq:developfrom
hayaksi1:fix/4770-fake-voice-recorder

Conversation

@hayaksi1

Copy link
Copy Markdown
Contributor

Content

FakeVoiceRecorder pushed a whole recording synchronously from a single suspending call, which made it both weaker and stricter than DefaultVoiceRecorder. It now models a cancellable recording job, the way the real recorder does.

Every level emission carried the same elapsed time and the emissions were coalesced by Molecule, so no intermediate recording state was observable. present - recording state - number of levels is limited had to compensate with skipItems(numberOfLevels / 2 - 1), a magic number that only worked because of the two-to-one coalescing ratio. Each level now advances the test time source and suspends for its share of the recording duration, so the states arrive one at a time, present - recording state asserts both of them, and that magic number is gone.

Call verification moved to the constructor lambdas used elsewhere in the repo, replacing the hand-rolled startedCount/stoppedCount/deletedCount and assertCalls. The counters discarded the argument, so nothing pinned that cancelling a recording stops it with cancelled = true; present - abort recording now asserts exactly that. givenThrowsSecurityException is replaced by a throwing startRecordResult, and Truth is no longer a dependency of the fixture module.

Finally, a second startRecord() no longer raises error("Previous recording was not cleared"), where DefaultVoiceRecorder only warns and returns, and stopping or deleting now terminates an in-flight emission loop the way cancelling the real recording job does — so a stale Recording state can no longer arrive after Finished, which is what made adding a delay to the fake break tests before.

No production code changes.

Motivation and context

Part of #4770.

Tests

Test-only change. DefaultVoiceMessageComposerPresenterTest is 28 tests green with the reworked fake.

Behavioural gains, rather than a rename: present - recording state now asserts the intermediate Recording(500ms, [0.1f]) as well as the final state; present - recording state - number of levels is limited no longer depends on the coalescing ratio; present - abort recording asserts stopRecord(cancelled = true), which the old counters could not see.

Run with ./gradlew :features:messages:impl:testDebugUnitTest.

Checked that the new lambdaError() defaults do not fire through FakeDefaultVoiceMessageComposerPresenterFactory by running MessagesPresenterTest unchanged, and that :libraries:voicerecorder:test and :features:messages:test still compile.

Tested devices

  • Physical
  • Emulator
  • OS version(s):

Checklist

  • I am aware of the etiquette.
  • This PR was made with the help of AI:
    • Yes. In this case, please request a review by Copilot.
    • No.
  • Changes have been tested on an Android device or Android emulator with API 24
  • UI change has been tested on both light and dark themes
  • Accessibility has been taken into account. See https://github.com/element-hq/element-x-android/blob/develop/CONTRIBUTING.md#accessibility
  • Pull request is based on the develop branch
  • Pull request title will be used in the release note, it clearly defines what will change for the user
  • Pull request includes screenshots or videos if containing UI changes
  • You've made a self review of your PR

FakeVoiceRecorder pushed a whole recording synchronously from a single
suspending call, which made it both weaker and stricter than
DefaultVoiceRecorder.

Every level emission carried the same elapsed time and the emissions were
coalesced by Molecule, so no intermediate recording state was observable and
the level-limit test had to skip half the emissions to compensate. Each level
now advances the test time source and suspends for its share of the recording
duration, so the states arrive one at a time and that magic number is gone.

Call verification moved to the constructor lambdas the rest of the repo uses,
in place of hand-rolled counters and assertCalls. The counters discarded the
argument, so nothing pinned that cancelling a recording stops it with
cancelled = true; that is now asserted. Truth is no longer needed by the
fixture module.

A second startRecord() no longer raises an error where production only warns
and returns, and stopping or deleting terminates an in-flight emission loop
the way cancelling the real recording job does, so a stale recording state can
no longer arrive after the recording has finished.
@hayaksi1
hayaksi1 requested a review from a team as a code owner August 17, 2026 14:30
@hayaksi1
hayaksi1 requested review from jmartinesp and removed request for a team August 17, 2026 14:30
@github-actions

Copy link
Copy Markdown
Contributor

Thank you for your contribution! Here are a few things to check in the PR to ensure it's reviewed as quickly as possible:

  • If your pull request adds a feature or modifies the UI, this should have an equivalent pull request in the Element X iOS repo unless it only affects an Android-only behaviour or is behind a disabled feature flag, since we need parity in both clients to consider a feature done. It will also need to be approved by our product and design teams before being merged, so it's usually a good idea to discuss the changes in a Github issue first and then start working on them once the approach has been validated.
  • Your branch should be based on origin/develop, at least when it was created.
  • The title of the PR will be used for release notes, so it needs to describe the change visible to the user.
  • The test pass locally running ./gradlew test.
  • The code quality check suite pass locally running ./gradlew runQualityChecks.
  • If you modified anything related to the UI, including previews, you'll have to run the Record screenshots GH action in your forked repo: that will generate compatible new screenshots. However, given Github Actions limitations, it will prevent the CI from running temporarily, until you upload a new commit after that one. To do so, just pull the latest changes and push an empty commit.

@github-actions github-actions Bot added the Z-Community-PR Issue is solved by a community member's PR label Aug 17, 2026
The level loop checked a single isRecording flag, which cannot tell "this
recording was cancelled" from "a later recording has started". After a cancel
followed by a new start, the first loop resumed, saw the flag set again, and
advanced the shared test time source, so the new recording reported the elapsed
time of both. Each recording now carries an id and its loop stops as soon as
that id is no longer the active one.
@hayaksi1
hayaksi1 force-pushed the fix/4770-fake-voice-recorder branch from d02a9e8 to e8730f5 Compare August 17, 2026 15:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Z-Community-PR Issue is solved by a community member's PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant