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
//! This crate provides a simple and efficient way to generate globally unique identifiers.

// This feature is needed to get the number of leading zeros in a number.
#![feature(core_intrinsics)]

/// The bowl is used for generating Oat values in unified way.
pub mod bowl;

/// The oats are globally unique identifiers.
pub mod oat;

/// The region moduele contains predefined regions
pub mod region;

#[cfg(test)]
mod tests {
    /// This module contains the implementation of the `bowl` module.
    mod bowl {
        use std::sync::Arc;
        use std::thread;

        use crate::bowl::{Bowl, GenerationBehavior, WrappedBowl};
        use crate::oat::Oat;

        /// Test function for generating Oat values using a WrappedBowl with normal generation behavior.
        #[test]
        fn test_wrapped_bowl_generate() {
            // Create a WrappedBowl instance with node id 1 and normal generation behavior
            let wrapped_bowl: WrappedBowl = WrappedBowl::of(1, GenerationBehavior::Normal, None);
            test_wrapped_bowl_generate_definition(wrapped_bowl)
        }

        /// Test function for generating Oat values using a WrappedBowl with lazy generation behavior.
        #[test]
        fn test_wrapped_bowl_generate_lazy() {
            // Create a WrappedBowl instance with node id 1 and lazy generation behavior
            let wrapped_bowl: WrappedBowl = WrappedBowl::of(1, GenerationBehavior::Lazy, None);
            test_wrapped_bowl_generate_definition(wrapped_bowl)
        }

        /// Test function for creating a Bowl with Realtime generation behavior and generating Oat values.
        #[test]
        fn test_bowl_new_seq_realtime() {
            let mut bowl: Bowl = Bowl::of(1, GenerationBehavior::Realtime, None);

            // Generate 10 Oat values with the Realtime generation behavior mode
            let oats: Vec<u16> = (0..10).map(|_| bowl.new_seq()).collect();

            // Check that the sequence numbers of the Oat values are increasing
            assert!(oats.windows(2).all(|window| window[0] < window[1]));
        }

        /// Test function for generating Oat values using a WrappedBowl in a multi-threaded environment.
        #[test]
        fn test_wrapped_bowl_in_multi_thread_env() {
            let wrapped: WrappedBowl = WrappedBowl::of(1, GenerationBehavior::Normal, None);
            let mut handles = vec![];

            for _ in 0..10 {
                let clone = wrapped.clone();
                handles.push(thread::spawn(move || {
                    clone.generate();
                }));
            }

            handles.into_iter().for_each(|h| h.join().unwrap());
        }

        /// Test function for generating Oat values using a WrappedBowl.
        fn test_wrapped_bowl_generate_definition(wrapped_bowl: WrappedBowl) {
            // Generate 10 Oat values
            let oats: Vec<Oat> = (0..4095).map(|_| wrapped_bowl.generate()).collect();

            // Check that the Oat values have the correct node id
            assert!(oats.iter().all(|oat| oat.node() == 1));

            // Check that the Oat values have increasing sequence numbers
            assert!(oats
                .windows(2)
                .all(|window| window[0].seq() < window[1].seq()));

            // Check if seq restarts
            let oat = wrapped_bowl.generate();
            assert_eq!(oat.seq(), 0);
            assert_eq!(oat.node(), 1);
            assert!(oat.timestamp() > oats.last().unwrap().timestamp())
        }
    }

    /// The `oat` module contains tests for the `Oat` struct.
    mod oat {
        use crate::oat::Oat;

        /// Test the `to_string` method of the `Oat` struct.
        #[test]
        fn test_to_string() {
            let oat = Oat::of(1, 3, 1671800400_000);
            let s = oat.to_string();

            assert_eq!(s, "X1AwCIGvFTGAA");
        }

        /// Test the `to_bytes` method of the `Oat` struct.
        #[test]
        fn test_to_bytes() {
            let oat = Oat::of(1, 3, 1671800400_000);
            let b: [u8; 9] = oat.to_bytes();

            assert_eq!(b, [1, 0x03, 0x0, 0x88, 0x1A, 0xF1, 0x53, 0x18, 0x0]);
        }

        /// Test the `into` method of the `Oat` struct.
        #[test]
        fn test_into_bytes() {
            let oat = Oat::of(1, 3, 1671800400_000);
            let b: [u8; 9] = oat.into();

            assert_eq!(b, [1, 0x03, 0x0, 0x88, 0x1A, 0xF1, 0x53, 0x18, 0x0]);
        }

        /// Test the `from_string` method of the `Oat` struct.
        #[test]
        fn test_from_string() {
            let oat = Oat::from_string("X1AwCIGvFTGAA").unwrap();

            assert_eq!(oat.node(), 1);
            assert_eq!(oat.seq(), 3);
            assert_eq!(oat.timestamp(), 1671800400_000);
        }

        /// Test the `from_string_unchecked` method of the `Oat` struct.
        #[test]
        fn test_from_string_unchecked() {
            let oat = Oat::from_string_unchecked("X1AwCIGvFTGAA");

            assert_eq!(oat.node(), 1);
            assert_eq!(oat.seq(), 3);
            assert_eq!(oat.timestamp(), 1671800400_000);
        }

        /// Test the `from_bytes` method of the `Oat` struct.
        #[test]
        fn test_from_bytes() {
            let oat = Oat::from_bytes([1, 0x03, 0x0, 0x88, 0x1A, 0xF1, 0x53, 0x18, 0x0]);

            assert_eq!(oat.node(), 1);
            assert_eq!(oat.seq(), 3);
            assert_eq!(oat.timestamp(), 1671800400_000);
        }

        /// Test the `from_bytes_ref` method of the `Oat` struct.
        #[test]
        fn test_from_bytes_ref() {
            let oat =
                Oat::from_bytes_ref(&[1, 0x03, 0x0, 0x88, 0x1A, 0xF1, 0x53, 0x18, 0x0]).unwrap();

            assert_eq!(oat.node(), 1);
            assert_eq!(oat.seq(), 3);
            assert_eq!(oat.timestamp(), 1671800400_000);
        }
    }
}