Merge branch 'master' into crypto-improvements

master
Damir Jelić 2020-12-08 15:06:14 +01:00
commit 8e53982bcd
9 changed files with 267 additions and 51 deletions

103
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,103 @@
name: CI
on:
push:
pull_request:
branches: [ master ]
env:
CARGO_TERM_COLOR: always
jobs:
style:
name: Check style
runs-on: ubuntu-latest
steps:
- name: Checkout the repo
uses: actions/checkout@v2
- name: Install rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
components: rustfmt
profile: minimal
override: true
- name: Cargo fmt
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
clippy:
name: Run clippy
needs: [style]
runs-on: ubuntu-latest
steps:
- name: Checkout the repo
uses: actions/checkout@v2
- name: Install rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
components: clippy
profile: minimal
override: true
- name: Clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: --all-targets -- -D warnings
test:
name: ${{ matrix.name }}
needs: [clippy]
runs-on: ${{ matrix.os || 'ubuntu-latest' }}
strategy:
matrix:
name:
- linux / stable
- linux / beta
- macOS / stable
- windows / stable-x86_64-msvc
include:
- name: linux / stable
- name: linux / beta
rust: beta
- name: macOS / stable
os: macOS-latest
- name: windows / stable-x86_64-msvc
os: windows-latest
target: x86_64-pc-windows-msvc
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Install rust
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust || 'stable' }}
target: ${{ matrix.target }}
profile: minimal
override: true
- name: Build
uses: actions-rs/cargo@v1
with:
command: build
- name: Test
uses: actions-rs/cargo@v1
with:
command: test

39
.github/workflows/coverage.yml vendored Normal file
View File

@ -0,0 +1,39 @@
name: Code coverage
on:
push:
branches: [ master ]
env:
CARGO_TERM_COLOR: always
jobs:
code_coverage:
name: Code Coverage
runs-on: "ubuntu-latest"
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install stable toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
- name: Install tarpaulin
uses: actions-rs/cargo@v1
with:
command: install
args: cargo-tarpaulin -f
- name: Run tarpaulin
uses: actions-rs/cargo@v1
with:
command: tarpaulin
args: --ignore-config --exclude-files "matrix_sdk/examples/*,matrix_sdk_common,matrix_sdk_test" --out Xml
- name: Upload to codecov.io
uses: codecov/codecov-action@v1

View File

@ -1,4 +1,4 @@
[![Build Status](https://img.shields.io/travis/matrix-org/matrix-rust-sdk.svg?style=flat-square)](https://travis-ci.org/matrix-org/matrix-rust-sdk) ![Build Status](https://img.shields.io/github/workflow/status/matrix-org/matrix-rust-sdk/CI?style=flat-square)
[![codecov](https://img.shields.io/codecov/c/github/matrix-org/matrix-rust-sdk/master.svg?style=flat-square)](https://codecov.io/gh/matrix-org/matrix-rust-sdk) [![codecov](https://img.shields.io/codecov/c/github/matrix-org/matrix-rust-sdk/master.svg?style=flat-square)](https://codecov.io/gh/matrix-org/matrix-rust-sdk)
[![License](https://img.shields.io/badge/License-Apache%202.0-yellowgreen.svg?style=flat-square)](https://opensource.org/licenses/Apache-2.0) [![License](https://img.shields.io/badge/License-Apache%202.0-yellowgreen.svg?style=flat-square)](https://opensource.org/licenses/Apache-2.0)
[![#matrix-rust-sdk](https://img.shields.io/badge/matrix-%23matrix--rust--sdk-blue?style=flat-square)](https://matrix.to/#/#matrix-rust-sdk:matrix.org) [![#matrix-rust-sdk](https://img.shields.io/badge/matrix-%23matrix--rust--sdk-blue?style=flat-square)](https://matrix.to/#/#matrix-rust-sdk:matrix.org)

View File

@ -74,6 +74,7 @@ use matrix_sdk_common::{
join_room_by_id, join_room_by_id_or_alias, kick_user, leave_room, Invite3pid, join_room_by_id, join_room_by_id_or_alias, kick_user, leave_room, Invite3pid,
}, },
message::{get_message_events, send_message_event}, message::{get_message_events, send_message_event},
profile::{get_avatar_url, get_display_name, set_avatar_url, set_display_name},
read_marker::set_read_marker, read_marker::set_read_marker,
receipt::create_receipt, receipt::create_receipt,
room::create_room, room::create_room,
@ -429,6 +430,117 @@ impl Client {
session.as_ref().cloned().map(|s| s.user_id) session.as_ref().cloned().map(|s| s.user_id)
} }
/// Fetches the display name of the owner of the client.
///
/// # Example
/// ```no_run
/// # use futures::executor::block_on;
/// # use matrix_sdk::Client;
/// # use url::Url;
/// # let homeserver = Url::parse("http://example.com").unwrap();
/// # block_on(async {
/// let user = "example";
/// let client = Client::new(homeserver).unwrap();
/// client.login(user, "password", None, None).await.unwrap();
///
/// if let Some(name) = client.display_name().await.unwrap() {
/// println!("Logged in as user '{}' with display name '{}'", user, name);
/// }
/// # })
/// ```
pub async fn display_name(&self) -> Result<Option<String>> {
let user_id = self.user_id().await.ok_or(Error::AuthenticationRequired)?;
let request = get_display_name::Request::new(&user_id);
let response = self.send(request).await?;
Ok(response.displayname)
}
/// Sets the display name of the owner of the client.
///
/// # Example
/// ```no_run
/// # use futures::executor::block_on;
/// # use matrix_sdk::Client;
/// # use url::Url;
/// # let homeserver = Url::parse("http://example.com").unwrap();
/// # block_on(async {
/// let user = "example";
/// let client = Client::new(homeserver).unwrap();
/// client.login(user, "password", None, None).await.unwrap();
///
/// client.set_display_name(Some("Alice")).await.expect("Failed setting display name");
/// # })
/// ```
pub async fn set_display_name(&self, name: Option<&str>) -> Result<()> {
let user_id = self.user_id().await.ok_or(Error::AuthenticationRequired)?;
let request = set_display_name::Request::new(&user_id, name);
self.send(request).await?;
Ok(())
}
/// Gets the mxc avatar url of the owner of the client, if set.
///
/// # Example
/// ```no_run
/// # use futures::executor::block_on;
/// # use matrix_sdk::Client;
/// # use url::Url;
/// # let homeserver = Url::parse("http://example.com").unwrap();
/// # block_on(async {
/// # let user = "example";
/// let client = Client::new(homeserver).unwrap();
/// client.login(user, "password", None, None).await.unwrap();
///
/// if let Some(url) = client.avatar_url().await.unwrap() {
/// println!("Your avatar's mxc url is {}", url);
/// }
/// # })
/// ```
pub async fn avatar_url(&self) -> Result<Option<String>> {
let user_id = self.user_id().await.ok_or(Error::AuthenticationRequired)?;
let request = get_avatar_url::Request::new(&user_id);
let response = self.send(request).await?;
Ok(response.avatar_url)
}
/// Sets the mxc avatar url of the client's owner. The avatar gets unset if `url` is `None`.
pub async fn set_avatar_url(&self, url: Option<&str>) -> Result<()> {
let user_id = self.user_id().await.ok_or(Error::AuthenticationRequired)?;
let request = set_avatar_url::Request::new(&user_id, url);
self.send(request).await?;
Ok(())
}
/// Upload and set the owning client's avatar.
///
/// The will upload the data produced by the reader to the homeserver's content repository, and
/// set the user's avatar to the mxc url for the uploaded file.
///
/// This is a convenience method for calling [`upload()`](#method.upload), followed by
/// [`set_avatar_url()`](#method.set_avatar_url).
///
/// # Example
/// ```no_run
/// # use std::{path::Path, fs::File, io::Read};
/// # use futures::executor::block_on;
/// # use matrix_sdk::Client;
/// # use url::Url;
/// # block_on(async {
/// # let homeserver = Url::parse("http://locahost:8080").unwrap();
/// # let client = Client::new(homeserver).unwrap();
/// let path = Path::new("/home/example/selfie.jpg");
/// let mut image = File::open(&path).unwrap();
///
/// client.upload_avatar(&mime::IMAGE_JPEG, &mut image).await.expect("Can't set avatar");
/// # })
/// ```
pub async fn upload_avatar<R: Read>(&self, content_type: &Mime, reader: &mut R) -> Result<()> {
let upload_response = self.upload(content_type, reader).await?;
self.set_avatar_url(Some(&upload_response.content_uri))
.await?;
Ok(())
}
/// Add `EventEmitter` to `Client`. /// Add `EventEmitter` to `Client`.
/// ///
/// The methods of `EventEmitter` are called when the respective `RoomEvents` occur. /// The methods of `EventEmitter` are called when the respective `RoomEvents` occur.
@ -792,7 +904,7 @@ impl Client {
since: Option<&str>, since: Option<&str>,
server: Option<&ServerName>, server: Option<&ServerName>,
) -> Result<get_public_rooms::Response> { ) -> Result<get_public_rooms::Response> {
let limit = limit.map(|n| UInt::from(n)); let limit = limit.map(UInt::from);
let request = assign!(get_public_rooms::Request::new(), { let request = assign!(get_public_rooms::Request::new(), {
limit, limit,

View File

@ -224,11 +224,9 @@ mod test {
use crate::{ use crate::{
identifiers::{room_id, user_id}, identifiers::{room_id, user_id},
push::Ruleset, push::Ruleset,
BaseClient, BaseClientConfig, Session, Session,
}; };
use matrix_sdk_test::{sync_response, SyncResponseFile};
#[tokio::test] #[tokio::test]
async fn test_store_client_state() { async fn test_store_client_state() {
let dir = tempdir().unwrap(); let dir = tempdir().unwrap();
@ -360,44 +358,4 @@ mod test {
// test that we have removed the correct room // test that we have removed the correct room
assert!(invited.is_empty()); assert!(invited.is_empty());
} }
#[tokio::test]
async fn test_client_sync_store() {
let dir = tempdir().unwrap();
let path: &Path = dir.path();
let session = Session {
access_token: "1234".to_owned(),
user_id: user_id!("@cheeky_monkey:matrix.org"),
device_id: "DEVICEID".into(),
};
// a sync response to populate our JSON store
let store = Box::new(JsonStore::open(path).unwrap());
let client =
BaseClient::new_with_config(BaseClientConfig::new().state_store(store)).unwrap();
client.restore_login(session.clone()).await.unwrap();
let mut response = sync_response(SyncResponseFile::Default);
// gather state to save to the db, the first time through loading will be skipped
client.receive_sync_response(&mut response).await.unwrap();
// now syncing the client will update from the state store
let store = Box::new(JsonStore::open(path).unwrap());
let client =
BaseClient::new_with_config(BaseClientConfig::new().state_store(store)).unwrap();
client.restore_login(session.clone()).await.unwrap();
// assert the synced client and the logged in client are equal
assert_eq!(*client.session().read().await, Some(session));
assert_eq!(
client.sync_token().await,
Some("s526_47314_0_7_1_1_1_11444_1".to_string())
);
assert_eq!(
*client.ignored_users.read().await,
vec![user_id!("@someone:example.org")]
);
}
} }

View File

@ -170,7 +170,7 @@ impl Account {
return Err(OlmError::SessionWedged(user_id, sender_key)); return Err(OlmError::SessionWedged(user_id, sender_key));
} }
} }
Err(e) => return Err(e.into()), Err(e) => return Err(e),
}; };
debug!("Decrypted a to-device event {:?}", event); debug!("Decrypted a to-device event {:?}", event);

View File

@ -138,7 +138,7 @@ mod test {
use crate::ReadOnlyAccount; use crate::ReadOnlyAccount;
#[tokio::test] #[tokio::test]
#[cfg(not(target_os = "macos"))] #[cfg(target_os = "linux")]
async fn expiration() { async fn expiration() {
let settings = EncryptionSettings { let settings = EncryptionSettings {
rotation_period_msgs: 1, rotation_period_msgs: 1,

View File

@ -316,8 +316,7 @@ mod test {
use matrix_sdk_common::{ use matrix_sdk_common::{
api::r0::keys::claim_keys::Response as KeyClaimResponse, api::r0::keys::claim_keys::Response as KeyClaimResponse,
identifiers::{user_id, DeviceIdBox, DeviceKeyAlgorithm, UserId}, identifiers::{user_id, DeviceIdBox, UserId},
instant::{Duration, Instant},
}; };
use matrix_sdk_test::async_test; use matrix_sdk_test::async_test;
@ -422,8 +421,13 @@ mod test {
// This test doesn't run on macos because we're modifying the session // This test doesn't run on macos because we're modifying the session
// creation time so we can get around the UNWEDGING_INTERVAL. // creation time so we can get around the UNWEDGING_INTERVAL.
#[async_test] #[async_test]
#[cfg(not(target_os = "macos"))] #[cfg(target_os = "linux")]
async fn session_unwedging() { async fn session_unwedging() {
use matrix_sdk_common::{
identifiers::DeviceKeyAlgorithm,
instant::{Duration, Instant},
};
let manager = session_manager().await; let manager = session_manager().await;
let bob = bob_account(); let bob = bob_account();
let (_, mut session) = bob.create_session_for(&manager.account).await; let (_, mut session) = bob.create_session_for(&manager.account).await;

View File

@ -380,7 +380,7 @@ mod test {
assert!(bob.is_done()); assert!(bob.is_done());
} }
#[cfg(not(target_os = "macos"))] #[cfg(target_os = "linux")]
#[tokio::test] #[tokio::test]
async fn timing_out() { async fn timing_out() {
let (alice_machine, bob) = setup_verification_machine().await; let (alice_machine, bob) = setup_verification_machine().await;