1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use {
    anyhow::anyhow,
    fidl_fuchsia_update::CommitStatusProviderProxy,
    fuchsia_zircon::{self as zx, AsHandleRef},
};

/// Whether the current system version is pending commit.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum CommitStatus {
    /// The current system version is pending commit.
    Pending,
    /// The current system version is already committed.
    Committed,
}

/// Queries the commit status using `provider`.
pub async fn query_commit_status(
    provider: &CommitStatusProviderProxy,
) -> Result<CommitStatus, anyhow::Error> {
    let event_pair = provider.is_current_system_committed().await?;
    match event_pair.wait_handle(zx::Signals::USER_0, zx::Time::INFINITE_PAST) {
        Ok(_) => Ok(CommitStatus::Committed),
        Err(zx::Status::TIMED_OUT) => Ok(CommitStatus::Pending),
        Err(status) => Err(anyhow!("unexpected status while asserting signal: {:?}", status)),
    }
}

#[cfg(test)]
mod tests {
    use {
        super::*,
        fidl::endpoints::create_proxy_and_stream,
        fidl_fuchsia_update::{CommitStatusProviderMarker, CommitStatusProviderRequest},
        fuchsia_async::{self as fasync},
        fuchsia_zircon::{HandleBased, Peered},
        futures::StreamExt,
    };

    // Verifies that query_commit_status returns the expected CommitStatus.
    #[fasync::run_singlethreaded(test)]
    async fn test_query_commit_status() {
        let (proxy, mut stream) = create_proxy_and_stream::<CommitStatusProviderMarker>().unwrap();
        let (p0, p1) = zx::EventPair::create();

        let _fidl_server = fasync::Task::local(async move {
            while let Some(Ok(req)) = stream.next().await {
                let CommitStatusProviderRequest::IsCurrentSystemCommitted { responder } = req;
                let () = responder.send(p1.duplicate_handle(zx::Rights::BASIC).unwrap()).unwrap();
            }
        });

        // When no signals are asserted, we should report Pending.
        assert_eq!(query_commit_status(&proxy).await.unwrap(), CommitStatus::Pending);

        // When USER_0 is asserted, we should report Committed.
        let () = p0.signal_peer(zx::Signals::NONE, zx::Signals::USER_0).unwrap();
        assert_eq!(query_commit_status(&proxy).await.unwrap(), CommitStatus::Committed,);
    }
}