Skip to main content

blobfs/
mock.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Mock implementation of blobfs for blobfs::Client.
6
7use fuchsia_hash::Hash;
8use futures::StreamExt as _;
9use std::collections::HashSet;
10use std::convert::TryInto as _;
11use zx::HandleBased as _;
12use {fidl_fuchsia_fxfs as ffxfs, fidl_fuchsia_io as fio};
13
14/// A testing server implementation of /blob.
15///
16/// Mock does not handle requests until instructed to do so.
17pub struct Mock {
18    pub(super) stream: fio::DirectoryRequestStream,
19    pub(super) reader_stream: ffxfs::BlobReaderRequestStream,
20    pub(super) creator_stream: ffxfs::BlobCreatorRequestStream,
21}
22
23impl Mock {
24    /// Consume the next BlobCreator request, verifying it is intended to create the blob identified
25    /// by `merkle`. Fail the request with `e`.
26    ///
27    /// # Panics
28    ///
29    /// Panics on error or assertion violation (unexpected requests or a mismatched open call)
30    pub async fn fail_create(&mut self, merkle: Hash, e: ffxfs::CreateBlobError) {
31        match self.creator_stream.next().await {
32            Some(Ok(ffxfs::BlobCreatorRequest::Create { hash, allow_existing, responder })) => {
33                assert_eq!(Hash::from(hash), merkle);
34                assert!(!allow_existing);
35                let () = responder.send(Err(e)).unwrap();
36            }
37            other => panic!("unexpected request: {other:?}"),
38        }
39    }
40
41    /// Consume the next BlobCreator request, verifying it is intended to create the blob identified
42    /// by `merkle` with the same `allow_existing`. Return a `BlobWriter` for validating the writes.
43    ///
44    /// # Panics
45    ///
46    /// Panics on error or assertion violation (unexpected requests or a mismatched open call)
47    pub async fn expect_create_blob(
48        &mut self,
49        merkle: Hash,
50        expected_allow_existing: bool,
51    ) -> BlobWriter {
52        match self.creator_stream.next().await {
53            Some(Ok(ffxfs::BlobCreatorRequest::Create { hash, allow_existing, responder })) => {
54                assert_eq!(Hash::from(hash), merkle);
55                assert_eq!(allow_existing, expected_allow_existing);
56                let (writer, stream) =
57                    fidl::endpoints::create_request_stream::<ffxfs::BlobWriterMarker>();
58                let () = responder.send(Ok(writer)).unwrap();
59                BlobWriter { stream, vmo: None }
60            }
61            other => panic!("unexpected request: {other:?}"),
62        }
63    }
64
65    /// Consume the next BlobReader request, verifying it is intended to open the blob identified
66    /// by `merkle`. Either serve the contents of `res.ok()` or fail the open with `res.err()`.
67    ///
68    /// # Panics
69    ///
70    /// Panics on error or assertion violation (unexpected requests or a mismatched open call)
71    pub async fn expect_open_blob(&mut self, merkle: Hash, res: Result<Vec<u8>, zx::Status>) {
72        match self.reader_stream.next().await {
73            Some(Ok(ffxfs::BlobReaderRequest::GetVmo { blob_hash, responder })) => {
74                assert_eq!(Hash::from(blob_hash), merkle);
75                match res {
76                    Ok(content) => {
77                        let vmo = zx::Vmo::create(content.len().try_into().unwrap()).unwrap();
78                        let () = vmo.write(&content, 0).unwrap();
79                        let () = responder.send(Ok(vmo)).unwrap();
80                    }
81                    Err(s) => {
82                        let () = responder.send(Err(s.into_raw())).unwrap();
83                    }
84                }
85            }
86            other => panic!("unexpected request: {other:?}"),
87        }
88    }
89
90    /// Consume the next BlobCreator request, verifying it is intended to check whether the blob
91    /// identified by `merkle` needs to be overwritten. Either respond with `res.ok()` or fail with
92    /// `res.err()`.
93    ///
94    /// # Panics
95    ///
96    /// Panics on error or assertion violation (unexpected requests or a mismatched call)
97    pub async fn expect_needs_overwrite(&mut self, merkle: Hash, res: Result<bool, zx::Status>) {
98        match self.creator_stream.next().await {
99            Some(Ok(ffxfs::BlobCreatorRequest::NeedsOverwrite { blob_hash, responder })) => {
100                assert_eq!(Hash::from(blob_hash), merkle);
101                match res {
102                    Ok(needs_overwrite) => {
103                        let () = responder.send(Ok(needs_overwrite)).unwrap();
104                    }
105                    Err(s) => {
106                        let () = responder.send(Err(s.into_raw())).unwrap();
107                    }
108                }
109            }
110            other => panic!("unexpected request: {other:?}"),
111        }
112    }
113
114    /// Consume N directory requests, verifying they are intended to determine whether the blobs
115    /// specified `readable` and `missing` are readable or not using `GetVmo`, responding to the
116    /// check based on which collection the hash is in.
117    ///
118    /// # Panics
119    ///
120    /// Panics on error or assertion violation (unexpected requests, request for unspecified blob)
121    pub async fn expect_readable_missing_checks(&mut self, readable: &[Hash], missing: &[Hash]) {
122        let mut readable = readable.iter().copied().collect::<HashSet<_>>();
123        let mut missing = missing.iter().copied().collect::<HashSet<_>>();
124
125        while !(readable.is_empty() && missing.is_empty()) {
126            match self.creator_stream.next().await {
127                Some(Ok(ffxfs::BlobCreatorRequest::NeedsOverwrite { blob_hash, responder })) => {
128                    let hash = Hash::from(blob_hash);
129                    if readable.remove(&hash) {
130                        let () = responder.send(Ok(false)).unwrap();
131                    } else if missing.remove(&hash) {
132                        let () = responder.send(Err(zx::Status::NOT_FOUND.into_raw())).unwrap();
133                    } else {
134                        panic!("Unexpected blob existance check for {hash}");
135                    }
136                }
137                other => panic!("unexpected request: {other:?}"),
138            }
139        }
140    }
141
142    /// Expects and handles a call to [`Client::filter_to_missing_blobs`].
143    /// Verifies the call intends to determine whether the blobs specified in `readable` and
144    /// `missing` are readable or not, responding to the check based on which collection the hash is
145    /// in.
146    ///
147    /// # Panics
148    ///
149    /// Panics on error or assertion violation (unexpected requests, request for unspecified blob)
150    pub async fn expect_filter_to_missing_blobs_with_readable_missing_ids(
151        &mut self,
152        readable: &[Hash],
153        missing: &[Hash],
154    ) {
155        self.expect_readable_missing_checks(readable, missing).await;
156    }
157
158    /// Asserts that the request stream closes without any further requests.
159    ///
160    /// # Panics
161    ///
162    /// Panics on error
163    pub async fn expect_done(mut self) {
164        match self.stream.next().await {
165            None => {}
166            Some(request) => panic!("unexpected request: {request:?}"),
167        }
168    }
169}
170
171/// A testing server implementation of fuchsia.fxfs/BlobWriter.
172pub struct BlobWriter {
173    stream: ffxfs::BlobWriterRequestStream,
174    vmo: Option<zx::Vmo>,
175}
176
177impl BlobWriter {
178    /// Asserts that the request stream closes without any further requests.
179    ///
180    /// # Panics
181    ///
182    /// Panics on error
183    pub async fn expect_done(mut self) {
184        match self.stream.next().await {
185            None => {}
186            Some(request) => panic!("unexpected request: {request:?}"),
187        }
188    }
189
190    /// Asserts that GetVmo is called with the indicated size.
191    ///
192    /// # Panics
193    ///
194    /// Panics on error
195    pub async fn expect_get_vmo(&mut self, expected_size: u64) -> &mut Self {
196        match self.stream.next().await {
197            Some(Ok(ffxfs::BlobWriterRequest::GetVmo { size, responder })) => {
198                assert_eq!(expected_size, size);
199                let vmo = zx::Vmo::create(expected_size).unwrap();
200                assert!(self.vmo.is_none());
201                self.vmo = Some(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap());
202                let () = responder.send(Ok(vmo)).unwrap();
203            }
204            req => panic!("unexpected request {req:?}"),
205        }
206        self
207    }
208
209    /// Asserts that BytesWritten is called and responds with a data integrity error.
210    ///
211    /// # Panics
212    ///
213    /// Panics on error
214    pub async fn fail_bytes_written(&mut self) -> &mut Self {
215        match self.stream.next().await {
216            Some(Ok(ffxfs::BlobWriterRequest::BytesReady { bytes_written: _, responder })) => {
217                let () = responder.send(Err(zx::Status::IO_DATA_INTEGRITY.into_raw())).unwrap();
218            }
219            req => panic!("unexpected request {req:?}"),
220        }
221        self
222    }
223
224    /// Asserts that `content` is written to this freshly created `BlobWriter` in a single
225    /// `BytesWritten`.
226    ///
227    /// # Panics
228    ///
229    /// Panics on error
230    pub async fn expect_payload(mut self, content: &[u8]) {
231        self.expect_get_vmo(content.len().try_into().unwrap()).await;
232        match self.stream.next().await {
233            Some(Ok(ffxfs::BlobWriterRequest::BytesReady { bytes_written, responder })) => {
234                assert_eq!(bytes_written, u64::try_from(content.len()).unwrap());
235                let vmo = self.vmo.unwrap();
236                let mut buf = vec![0; content.len()];
237                let () = vmo.read(&mut buf, 0).unwrap();
238                assert_eq!(content, &buf[..content.len()]);
239                let () = responder.send(Ok(())).unwrap();
240            }
241            req => panic!("unexpected request {req:?}"),
242        }
243    }
244}