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
// Copyright 2019 The Exonum Team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use chrono::{DateTime, Duration, TimeZone, Utc}; use std::sync::{Arc, RwLock}; /// A helper trait that provides the node with a current time. pub trait TimeProvider: Send + Sync + ::std::fmt::Debug { /// Returns the current time. fn current_time(&self) -> DateTime<Utc>; } #[derive(Debug)] /// Provider of system time. pub struct SystemTimeProvider; impl TimeProvider for SystemTimeProvider { fn current_time(&self) -> DateTime<Utc> { Utc::now() } } /// Mock time provider for service testing. /// /// In terms of use, the mock time provider is similar to [`Arc`]; that is, clones of the provider /// control the same time record as the original instance. Therefore, to use the mock provider, /// one may clone its instance and use the clone to construct a [`TimeService`], /// while keeping the original instance to adjust the time reported to the validators /// along various test scenarios. /// /// # Examples /// /// ``` /// # extern crate exonum; /// # extern crate exonum_testkit; /// # extern crate exonum_time; /// # extern crate chrono; /// use chrono::{Utc, Duration, TimeZone}; /// use exonum::helpers::Height; /// use exonum_testkit::TestKitBuilder; /// use exonum_time::{time_provider::MockTimeProvider, schema::TimeSchema, TimeService}; /// /// # fn main() { /// let mock_provider = MockTimeProvider::default(); /// let mut testkit = TestKitBuilder::validator() /// .with_service(TimeService::with_provider(mock_provider.clone())) /// .create(); /// mock_provider.add_time(Duration::seconds(15)); /// testkit.create_blocks_until(Height(2)); /// /// // The time reported by the mock time provider is reflected by the service. /// let snapshot = testkit.snapshot(); /// let schema = TimeSchema::new(&snapshot); /// assert_eq!( /// Some(Utc.timestamp(15, 0)), /// schema.time().get().map(|time| time) /// ); /// # } /// ``` /// /// [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html /// [`TimeService`]: ../struct.TimeService.html #[derive(Debug, Clone)] pub struct MockTimeProvider { /// Local time value. time: Arc<RwLock<DateTime<Utc>>>, } impl Default for MockTimeProvider { /// Initializes the provider with the time set to the Unix epoch start. fn default() -> Self { Self::new(Utc.timestamp(0, 0)) } } impl MockTimeProvider { /// Creates a new `MockTimeProvider` with time value equal to `time`. pub fn new(time: DateTime<Utc>) -> Self { Self { time: Arc::new(RwLock::new(time)), } } /// Gets the time value currently reported by the provider. pub fn time(&self) -> DateTime<Utc> { *self.time.read().unwrap() } /// Sets the time value to `new_time`. pub fn set_time(&self, new_time: DateTime<Utc>) { let mut time = self.time.write().unwrap(); *time = new_time; } /// Adds `duration` to the value of `time`. pub fn add_time(&self, duration: Duration) { let mut time = self.time.write().unwrap(); *time = *time + duration; } } impl TimeProvider for MockTimeProvider { fn current_time(&self) -> DateTime<Utc> { self.time() } } impl From<MockTimeProvider> for Box<dyn TimeProvider> { fn from(mock_time_provider: MockTimeProvider) -> Self { Box::new(mock_time_provider) as Box<dyn TimeProvider> } }