/*
==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--

Namaste

Copyright (C) 2019, 2021-2023  Anonymous

There are several releases over multiple years,
they are listed as ranges, such as: "2021-2023".

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.

::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--
*/

//! # Tests

#![cfg(any(target_os = "linux", target_os = "l4re", target_os = "android", target_os = "windows"))]

use {
    core::{
        sync::atomic::{AtomicUsize, Ordering},
        time::Duration,
    },
    std::{
        sync::Arc,
        thread,
        time::Instant,
    },
    blackhole::{BlackHole, OneTime},
    namaste::Result,
    zeros::keccak::Hash,
};

const HALF_THREAD_COUNT: usize = 5;
const THREAD_COUNT: usize = HALF_THREAD_COUNT * 2;
const TIMEOUT: Duration = Duration::from_millis(500);
const DELAY: Duration = Duration::from_millis(100);
const SMALL_DELAY: Duration = Duration::from_millis(50);
const ORDERING: Ordering = Ordering::Relaxed;

#[test]
fn tests() -> Result<()> {
    assert!(SMALL_DELAY < DELAY);

    let ids = (0..THREAD_COUNT).into_iter().map(|i| Hash::Sha3_512.hash([
        namaste::ID.as_bytes(),
        &[
            0xa2, 0x56, 0x0c, 0xcd, 0x5f, 0xb3, 0x9d, 0x93, 0xd4, 0x1c, 0xe2, 0x0a, 0x5d, 0x12, 0xeb, 0xaf, 0xe8, 0x3f, 0x34, 0x0e, 0x8f, 0x74,
            0x93, 0x20, 0x76, 0xb6, 0xaa, 0x7b, 0x54, 0x8f, 0x93, 0x34, 0xe4, 0x8d, 0x85, 0xc1, 0x25, 0x64, 0xdb, 0xfd, 0x0f, 0x93, 0x53, 0x10,
            0x28, 0x7e, 0xfd, 0x4d, 0x1e, 0x23, 0xd7, 0x9b, 0xa1, 0xe7, 0x13, 0x30, 0xcb, 0x9c, 0x16, 0xdb, 0x85, 0x85, 0x83, 0x7b,
        ],
        &(i % HALF_THREAD_COUNT).to_be_bytes(),
    ]));

    let counter = Arc::new(AtomicUsize::new(usize::min_value()));
    let blackhole = BlackHole::make(THREAD_COUNT)?;
    let start = Instant::now();
    for (index, id) in ids.enumerate() {
        let counter = counter.clone();
        assert!(blackhole.throw(OneTime::new(move || {
            if index < HALF_THREAD_COUNT {
                let _namaste = namaste::make(id).unwrap();
                thread::sleep(DELAY);
            } else {
                thread::sleep(SMALL_DELAY);
                let _namaste = namaste::make_wait(id, TIMEOUT).unwrap();
            }
            counter.fetch_add(1, ORDERING);
        }))?.is_none());
    }

    blackhole.escape_on_idle()?;
    let duration = Instant::now().duration_since(start).as_millis();
    assert_eq!(counter.load(ORDERING), THREAD_COUNT);
    assert!(duration >= DELAY.as_millis() && duration <= DELAY.as_millis().checked_mul(HALF_THREAD_COUNT.try_into().unwrap()).unwrap());

    Ok(())
}
