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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.

//! Test for Tock KV System capsules.
//!
//! This capsule implements the tests for KV system libraries in Tock.
//! This is originally written to test TicKV.
//!
//!    hil::flash
//!
//! The tests can be enabled by adding this line to the `main()`
//!
//! ```rust,ignore
//! tickv_test::run_tickv_tests(kvstore)
//! ```
//!
//! You should then see the following output
//!
//! ```text
//! ---Starting TicKV Tests---
//! Key: [18, 52, 86, 120, 154, 188, 222, 240] with value [16, 32, 48] was added
//! Now retrieving the key
//! Key: [18, 52, 86, 120, 154, 188, 222, 240] with value [16, 32, 48, 0] was retrieved
//! Removed Key: [18, 52, 86, 120, 154, 188, 222, 240]
//! Try to read removed key: [18, 52, 86, 120, 154, 188, 222, 240]
//! Unable to find key: [18, 52, 86, 120, 154, 188, 222, 240]
//! Let's start a garbage collection
//! Finished garbage collection
//! ---Finished TicKV Tests---
//! ```

use crate::tickv::{KVSystem, KVSystemClient, KeyType};
use core::cell::Cell;
use core::marker::PhantomData;
use kernel::debug;
use kernel::utilities::cells::{MapCell, TakeCell};
use kernel::utilities::leasable_buffer::SubSliceMut;
use kernel::ErrorCode;

#[derive(Clone, Copy, PartialEq)]
enum CurrentState {
    Normal,
    ExpectGetValueFail,
}

pub struct KVSystemTest<'a, S: KVSystem<'static>, T: KeyType> {
    kv_system: &'a S,
    phantom: PhantomData<&'a T>,
    value: MapCell<SubSliceMut<'static, u8>>,
    ret_buffer: TakeCell<'static, [u8]>,
    state: Cell<CurrentState>,
}

impl<'a, S: KVSystem<'static>, T: KeyType> KVSystemTest<'a, S, T> {
    pub fn new(
        kv_system: &'a S,
        value: SubSliceMut<'static, u8>,
        static_buf: &'static mut [u8; 4],
    ) -> KVSystemTest<'a, S, T> {
        debug!("---Starting TicKV Tests---");

        Self {
            kv_system,
            phantom: PhantomData,
            value: MapCell::new(value),
            ret_buffer: TakeCell::new(static_buf),
            state: Cell::new(CurrentState::Normal),
        }
    }
}

impl<'a, S: KVSystem<'static, K = T>, T: KeyType + core::fmt::Debug> KVSystemClient<T>
    for KVSystemTest<'a, S, T>
{
    fn generate_key_complete(
        &self,
        result: Result<(), ErrorCode>,
        _unhashed_key: SubSliceMut<'static, u8>,
        key_buf: &'static mut T,
    ) {
        match result {
            Ok(()) => {
                debug!("Generated key: {:?}", key_buf);
                debug!("Now appending the key");
                self.kv_system
                    .append_key(key_buf, self.value.take().unwrap())
                    .unwrap();
            }
            Err(e) => {
                panic!("Error adding key: {:?}", e);
            }
        }
    }

    fn append_key_complete(
        &self,
        result: Result<(), ErrorCode>,
        key: &'static mut T,
        value: SubSliceMut<'static, u8>,
    ) {
        match result {
            Ok(()) => {
                debug!("Key: {:?} with value {:?} was added", key, value);
                debug!("Now retrieving the key");
                self.kv_system
                    .get_value(key, SubSliceMut::new(self.ret_buffer.take().unwrap()))
                    .unwrap();
            }
            Err(e) => {
                panic!("Error adding key: {:?}", e);
            }
        }
    }

    fn get_value_complete(
        &self,
        result: Result<(), ErrorCode>,
        key: &'static mut T,
        ret_buf: SubSliceMut<'static, u8>,
    ) {
        match result {
            Ok(()) => {
                debug!("Key: {:?} with value {:?} was retrieved", key, ret_buf);
                self.ret_buffer.replace(ret_buf.take());
                self.kv_system.invalidate_key(key).unwrap();
            }
            Err(e) => {
                if self.state.get() == CurrentState::ExpectGetValueFail {
                    // We expected this failure
                    debug!("Unable to find key: {:?}", key);
                    self.state.set(CurrentState::Normal);

                    debug!("Let's start a garbage collection");
                    self.kv_system.garbage_collect().unwrap();
                } else {
                    panic!("Error finding key: {:?}", e);
                }
            }
        }
    }

    fn invalidate_key_complete(&self, result: Result<(), ErrorCode>, key: &'static mut T) {
        match result {
            Ok(()) => {
                debug!("Removed Key: {:?}", key);

                debug!("Try to read removed key: {:?}", key);
                self.state.set(CurrentState::ExpectGetValueFail);
                self.kv_system
                    .get_value(key, SubSliceMut::new(self.ret_buffer.take().unwrap()))
                    .unwrap();
            }
            Err(e) => {
                panic!("Error invalidating key: {:?}", e);
            }
        }
    }

    fn garbage_collect_complete(&self, result: Result<(), ErrorCode>) {
        match result {
            Ok(()) => {
                debug!("Finished garbage collection");
                debug!("---Finished TicKV Tests---");
            }
            Err(e) => {
                panic!("Error running garbage collection: {:?}", e);
            }
        }
    }
}