Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
os: [ubuntu-22.04, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: ./.github/actions/setup-rust
with:
cache-key: test
Expand Down Expand Up @@ -63,7 +63,7 @@ jobs:
artifact: windows
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: ./.github/actions/setup-rust
with:
cache-key: release
Expand All @@ -79,7 +79,7 @@ jobs:
- name: Bundle tinyclient.app
if: matrix.artifact == 'macos'
run: packaging/bundle.sh --no-build
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v6
if: matrix.artifact == 'macos'
with:
name: tinyclient-macos
Expand All @@ -94,7 +94,7 @@ jobs:
desktop-file-validate packaging/linux/dev.tinyclient.app.desktop
dpkg-deb --info dist/tinyclient-ci-linux-x86_64.deb
dpkg-deb --contents dist/tinyclient-ci-linux-x86_64.deb
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v6
if: matrix.artifact == 'linux-x86_64'
with:
name: tinyclient-linux-x86_64
Expand All @@ -110,7 +110,7 @@ jobs:
target/release/mediad.exe, `
target/release/tinyclient-login.exe `
-DestinationPath tinyclient-windows-x86_64.zip
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v6
if: matrix.artifact == 'windows'
with:
name: tinyclient-windows-x86_64
Expand Down
24 changes: 12 additions & 12 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Name the files
run: packaging/ci-label.sh >> "$GITHUB_ENV"
- name: Reuse binaries built by CI
Expand Down Expand Up @@ -72,7 +72,7 @@ jobs:
test "$out" = "$program $VERSION"
done
hdiutil detach "$mnt"
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v6
with:
name: release-macos
path: dist/*
Expand All @@ -91,7 +91,7 @@ jobs:
artifact: linux-aarch64
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Name the files
run: packaging/ci-label.sh >> "$GITHUB_ENV"
- name: Reuse binaries built by CI
Expand Down Expand Up @@ -120,7 +120,7 @@ jobs:
run: |
eval "$(packaging/linux/tools.sh)"
packaging/linux.sh --no-build "$LABEL"
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v6
with:
name: release-linux-${{ matrix.arch }}
path: dist/*
Expand Down Expand Up @@ -167,10 +167,10 @@ jobs:
kind: rpm
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Name the files
run: packaging/ci-label.sh >> "$GITHUB_ENV"
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v7
with:
name: release-linux-${{ matrix.arch }}
path: dist
Expand All @@ -180,7 +180,7 @@ jobs:
windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Name the files
shell: bash
run: packaging/ci-label.sh >> "$GITHUB_ENV"
Expand Down Expand Up @@ -215,7 +215,7 @@ jobs:
- name: Smoke test the installer and the zip
shell: pwsh
run: packaging/windows/smoke.ps1 -Label $env:LABEL -Version $env:VERSION
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v6
with:
name: release-windows
path: dist/*
Expand All @@ -226,10 +226,10 @@ jobs:
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Name the files
run: packaging/ci-label.sh >> "$GITHUB_ENV"
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v7
with:
pattern: release-*
path: dist
Expand All @@ -246,11 +246,11 @@ jobs:
sha256sum tinyclient-* > SHA256SUMS
sha256sum -c SHA256SUMS
ls -l
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v6
with:
name: release-all
path: dist/*
- uses: softprops/action-gh-release@v2
- uses: softprops/action-gh-release@v3
if: github.ref_type == 'tag' && (github.event_name == 'push' || inputs.publish)
with:
files: dist/*
2 changes: 1 addition & 1 deletion .github/workflows/site.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
# The site promises it loads nothing from anywhere else; fail the deploy if a page would.
- name: No third-party requests
run: |
Expand Down
24 changes: 24 additions & 0 deletions crates/chatd/src/adapters/teams/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,17 @@ fn card_title(card: &Value, depth: u8) -> Option<String> {
}
}

/// The shell's `client_id` on a copy of a message we sent. Teams names it `clientmessageid`,
/// as text or as a number, and the store replaces the pending row only when it comes back on
/// the message. Someone else's copy, or an id that is already the message's own, is not one.
fn echoed_client_id(raw: &Value, outgoing: bool, id: &str) -> Option<String> {
if !outgoing {
return None;
}
let echoed = id_of(&raw["clientmessageid"]);
(!echoed.is_empty() && echoed != id).then_some(echoed)
}

/// `Event/Call`: `<partlist type="ended" …><part …><duration>125</duration></part></partlist>`.
fn call_line(content: &str) -> String {
let longest = inside(content, "duration").iter().filter_map(|text| text.trim().parse::<f64>().ok()).fold(0., f64::max);
Expand Down Expand Up @@ -446,6 +457,7 @@ impl Teams {
message.text = "Unsupported message".to_owned();
message.kind = MessageKind::Unsupported { link: self.message_link(conversation, &id) };
}
message.client_id = echoed_client_id(raw, message.outgoing, &id);
Read::Message(Box::new(message))
}

Expand Down Expand Up @@ -889,6 +901,18 @@ mod tests {
assert_eq!((attached.thumb.as_ref().map(|thumb| thumb.id.clone()), media.len()), (Some(attached.file.id.clone()), 1));
}

#[test]
fn a_copy_of_our_message_keeps_the_client_id_teams_echoes() {
let raw = json!({"id": "1760001000000", "clientmessageid": "1760000999000111"});
assert_eq!(echoed_client_id(&raw, true, "1760001000000").as_deref(), Some("1760000999000111"));
// Teams writes the same id as a number in some payloads.
let numbered = json!({"id": "1760001000000", "clientmessageid": 1760000999000111u64});
assert_eq!(echoed_client_id(&numbered, true, "1760001000000").as_deref(), Some("1760000999000111"));
assert_eq!(echoed_client_id(&raw, false, "1760001000000"), None);
let same = json!({"id": "1760001000000", "clientmessageid": "1760001000000"});
assert_eq!(echoed_client_id(&same, true, "1760001000000"), None);
}

#[test]
fn reaction_keys_read_as_emoji_and_back() {
assert_eq!((emoji_of("like"), emoji_of("heart"), emoji_of("sad")), ("👍".to_owned(), "❤️".to_owned(), "🙁".to_owned()));
Expand Down
43 changes: 39 additions & 4 deletions crates/mediad/tests/play.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//! replaced by a file (`TINYCLIENT_MEDIAD_SINK`) that drains at a device's pace.

use protocol::{read_frame, write_frame, PlayerEvent, PlayerRequest};
use std::io::BufReader;
use std::io::{BufReader, Write};
use std::path::{Path, PathBuf};
use std::process::{Child, ChildStdout, Command, Stdio};
use std::time::{Duration, Instant};
Expand All @@ -11,6 +11,36 @@ fn fixture(name: &str) -> String {
Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures").join(name).display().to_string()
}

/// A silent WAV of `seconds`. The fixtures are about a second long, which a busy machine can
/// play out before the thread reading stdin is scheduled, so a pause test needs a longer one.
fn silence(path: &Path, seconds: u32) {
let rate = 48_000u32;
let channels = 2u16;
let data = rate * seconds * u32::from(channels) * 2;
let mut file = std::fs::File::create(path).unwrap();
let mut head = Vec::with_capacity(44);
head.extend_from_slice(b"RIFF");
head.extend_from_slice(&(36 + data).to_le_bytes());
head.extend_from_slice(b"WAVEfmt ");
head.extend_from_slice(&16u32.to_le_bytes());
head.extend_from_slice(&1u16.to_le_bytes());
head.extend_from_slice(&channels.to_le_bytes());
head.extend_from_slice(&rate.to_le_bytes());
head.extend_from_slice(&(rate * u32::from(channels) * 2).to_le_bytes());
head.extend_from_slice(&(channels * 2).to_le_bytes());
head.extend_from_slice(&16u16.to_le_bytes());
head.extend_from_slice(b"data");
head.extend_from_slice(&data.to_le_bytes());
file.write_all(&head).unwrap();
let piece = vec![0u8; 48_000];
let mut left = data;
while left > 0 {
let n = (left as usize).min(piece.len());
file.write_all(&piece[..n]).unwrap();
left -= n as u32;
}
}

struct Player {
child: Child,
out: BufReader<ChildStdout>,
Expand Down Expand Up @@ -74,16 +104,21 @@ fn a_voice_message_plays_to_its_end_and_the_process_goes() {

#[test]
fn closing_stdin_pauses_and_says_where() {
let mut player = Player::open("pause", &fixture("tone.opus"), 0, 100);
assert!(matches!(player.next(), PlayerEvent::Playing { .. }));
let wav = std::env::temp_dir().join(format!("tinyclient-play-{}-pause.wav", std::process::id()));
silence(&wav, 30);
let mut player = Player::open("pause", &wav.display().to_string(), 0, 100);
let PlayerEvent::Playing { duration, .. } = player.next() else { panic!("not playing") };
assert!(duration >= 30_000, "{duration} ms");
let playing = Instant::now();
std::thread::sleep(Duration::from_millis(400));
// Measured, not assumed: a busy machine stretches the sleep, and the sound plays on meanwhile.
let played = playing.elapsed().as_millis() as u32;
drop(player.child.stdin.take());
let PlayerEvent::Stopped { at } = player.next() else { panic!("did not stop") };
let event = player.next();
let PlayerEvent::Stopped { at } = event else { panic!("did not stop: {event:?}") };
assert!((played.saturating_sub(100)..=played + 100).contains(&at), "stopped at {at} ms after {played} ms of playing");
player.exits_cleanly();
let _ = std::fs::remove_file(&wav);
}

#[test]
Expand Down
Loading