mock_verifier/
lib.rs

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// Copyright 2021 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 fidl_fuchsia_update_verify as fidl;
use fuchsia_async::Task;
use futures::{future, FutureExt as _, StreamExt as _};
use std::sync::Arc;

/// A call hook that can be used to inject responses into the Verifier service.
pub trait Hook: Send + Sync {
    /// Describes what the verify call will return.
    fn verify(
        &self,
        options: fidl::VerifyOptions,
    ) -> future::BoxFuture<'static, fidl::VerifierVerifyResult>;
}

impl<F> Hook for F
where
    F: Fn(fidl::VerifyOptions) -> fidl::VerifierVerifyResult + Send + Sync,
{
    fn verify(
        &self,
        options: fidl::VerifyOptions,
    ) -> future::BoxFuture<'static, fidl::VerifierVerifyResult> {
        future::ready(self(options)).boxed()
    }
}

pub struct MockVerifierService {
    call_hook: Box<dyn Hook>,
}

impl MockVerifierService {
    /// Creates a new MockVerifierService with a given callback to run per call to the service.
    pub fn new(hook: impl Hook + 'static) -> Self {
        Self { call_hook: Box::new(hook) }
    }

    /// Spawns an `fasync::Task` which serves fuchsia.update.verify/BlobfsVerifier.
    pub fn spawn_blobfs_verifier_service(self: Arc<Self>) -> (fidl::BlobfsVerifierProxy, Task<()>) {
        let (proxy, stream) =
            ::fidl::endpoints::create_proxy_and_stream::<fidl::BlobfsVerifierMarker>();

        let task = Task::spawn(self.run_blobfs_verifier_service(stream));

        (proxy, task)
    }

    /// Spawns an `fasync::Task` which serves fuchsia.update.verify/NetstackVerifier.
    pub fn spawn_netstack_verifier_service(
        self: Arc<Self>,
    ) -> (fidl::NetstackVerifierProxy, Task<()>) {
        let (proxy, stream) =
            ::fidl::endpoints::create_proxy_and_stream::<fidl::NetstackVerifierMarker>();

        let task = Task::spawn(self.run_netstack_verifier_service(stream));

        (proxy, task)
    }

    /// Serves fuchsia.update.verify/BlobfsVerifier.Verify requests on the given request stream.
    pub async fn run_blobfs_verifier_service(
        self: Arc<Self>,
        stream: fidl::BlobfsVerifierRequestStream,
    ) {
        let Self { call_hook } = &*self;
        stream
            .for_each(|request| match request.expect("received verifier request") {
                fidl::BlobfsVerifierRequest::Verify { options, responder } => call_hook
                    .verify(options)
                    .map(|res| responder.send(res).expect("sent verifier response")),
            })
            .await
    }

    /// Serves fuchsia.update.verify/NetstackVerifier.Verify requests on the given request stream.
    pub async fn run_netstack_verifier_service(
        self: Arc<Self>,
        stream: fidl::NetstackVerifierRequestStream,
    ) {
        let Self { call_hook } = &*self;
        stream
            .for_each(|request| match request.expect("received verifier request") {
                fidl::NetstackVerifierRequest::Verify { options, responder } => call_hook
                    .verify(options)
                    .map(|res| responder.send(res).expect("sent verifier response")),
            })
            .await
    }

    /// Spawns an `fasync::Task` which serves fuchsia.update.verify service.
    pub fn spawn_healthcheck_service(self: Arc<Self>) -> (fidl::VerifierProxy, Task<()>) {
        let (proxy, stream) = ::fidl::endpoints::create_proxy_and_stream::<fidl::VerifierMarker>();

        let task = Task::spawn(self.run_healthcheck_service(stream));

        (proxy, task)
    }

    /// Serves fuchsia.update.verify/Verifier.Verify
    pub async fn run_healthcheck_service(self: Arc<Self>, stream: fidl::VerifierRequestStream) {
        let Self { call_hook } = &*self;
        stream
            .for_each(|request| match request.expect("received verifier request") {
                fidl::VerifierRequest::Verify { options, responder } => call_hook
                    .verify(options)
                    .map(|res| responder.send(res).expect("sent verifier response")),
            })
            .await
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use fidl_fuchsia_update_verify::VerifyError;
    use fuchsia_async as fasync;
    use std::sync::atomic::{AtomicU32, Ordering};

    #[fasync::run_singlethreaded(test)]
    async fn test_mock_verifier() {
        let mock = Arc::new(MockVerifierService::new(|_| Ok(())));
        let (proxy, _server) = mock.spawn_healthcheck_service();

        let verify_result = proxy.verify(&Default::default()).await.expect("made fidl call");

        assert_eq!(verify_result, Ok(()));
    }

    #[fasync::run_singlethreaded(test)]
    async fn test_mock_verifier_fails() {
        let mock = Arc::new(MockVerifierService::new(|_| Err(VerifyError::Internal)));
        let (proxy, _server) = mock.spawn_healthcheck_service();

        let verify_result = proxy.verify(&Default::default()).await.expect("made fidl call");

        assert_eq!(verify_result, Err(VerifyError::Internal));
    }

    #[fasync::run_singlethreaded(test)]
    async fn test_mock_verifier_with_external_state() {
        let called = Arc::new(AtomicU32::new(0));
        let called_clone = Arc::clone(&called);
        let mock = Arc::new(MockVerifierService::new(move |_| {
            called_clone.fetch_add(1, Ordering::SeqCst);
            Ok(())
        }));
        let (proxy, _server) = mock.spawn_healthcheck_service();

        let verify_result = proxy.verify(&Default::default()).await.expect("made fidl call");

        assert_eq!(verify_result, Ok(()));
        assert_eq!(called.load(Ordering::SeqCst), 1);
    }
}