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
// 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,
    fuchsia_async::Task,
    futures::{future, FutureExt as _, StreamExt as _},
    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>().unwrap();

        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>().unwrap();

        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
    }
}

#[cfg(test)]
mod tests {
    use {
        super::*,
        fidl_fuchsia_update_verify::VerifyError,
        fuchsia_async as fasync,
        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_blobfs_verifier_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_blobfs_verifier_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_blobfs_verifier_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);
    }
}