From bec55635f9d9dbc70c29be595d4c9726b64cb3c1 Mon Sep 17 00:00:00 2001 From: Andrew Conlin Date: Wed, 18 Dec 2024 12:28:00 +0000 Subject: [PATCH 1/3] [2024-12-18] Adding tests for new syntax - Moving old tests into `testInitSyntax.py` - This syntax is still supported - `testInitSyntax.py` also tests calling tic() before toc(), which is not possible with the setup of `test.py`. This should be moved to its own file soon. --- tests/test.py | 107 +++++++++++++++++++++------------------- tests/testInitSyntax.py | 82 ++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 52 deletions(-) create mode 100644 tests/testInitSyntax.py diff --git a/tests/test.py b/tests/test.py index b019fd2..7fbfdd4 100644 --- a/tests/test.py +++ b/tests/test.py @@ -1,43 +1,41 @@ import pytest -import tictoc +from tictoc import tic,toc import time import math - -@pytest.fixture -def t(): - t = tictoc.init() - return t - - class testFunctionality: - def testBasic(self, t): - t.tic() + def testBasic(self): + tic() print("test") - t.toc() - assert t.results.seconds > 0 + results = toc() + assert results.seconds > 0 - def testOverwrite(self, t): - t.tic() + def testMultipleGlobalCalls(self): + tic() print("test") - t.toc() - firstResult = t.results.seconds + results = toc() print("test2") - t.toc() - secondResult = t.results.seconds + results2 = toc() - assert firstResult < secondResult + assert results.seconds < results2.seconds + + def testMultipleCalls(self): + first = tic() + print("test") + second = tic() + print("test2") + secondResult = toc(second).seconds + firstResult = toc(first).seconds + + assert firstResult > secondResult class testInvalid: - def testNoInit(self): + def testNonTicInputForToc(self): with pytest.raises(Exception): - t.tic() - - def testTocBeforeTic(self, t): - with pytest.raises(Exception): - t.toc() - + tic() + print("test") + toc(1) @pytest.mark.parametrize("sleepTime", [0.05, 0.5, 1]) class testAccuracy: @@ -45,42 +43,47 @@ class testAccuracy: def tol(self): return 0.0006 - def testSingleCall(self, t, sleepTime, tol): - t.tic() + def testSingleCall(self, sleepTime, tol): + tic() time.sleep(sleepTime) - t.toc() - assert (t.results.seconds > sleepTime) & ( - t.results.seconds < (t.results.seconds + tol) - ) + results = toc() + assert (results.seconds < sleepTime+tol) - def testMultipleCalls(self, t, sleepTime, tol): - t.tic() + def testMultipleGlobalCalls(self, sleepTime, tol): + tic() time.sleep(sleepTime) - t.toc() + toc() time.sleep(sleepTime) - t.toc() - assert (t.results.seconds > sleepTime * 2) & ( - t.results.seconds < (t.results.seconds + tol) - ) + results = toc() + assert (results.seconds < (sleepTime * 2)+tol) + def testMultipleCalls(self, sleepTime, tol): + first = tic() + time.sleep(sleepTime) + second = tic() + time.sleep(sleepTime) + results2 = toc(second) + results = toc(first) + assert (results.seconds < (sleepTime * 2)+tol) + assert (results2.seconds < sleepTime+tol) class testConsistency: - def testMicros(self, t): - t.tic() + def testMicros(self): + tic() print("test") - t.toc() - assert t.results.micros == (math.floor(t.results.nanos * pow(10, -3))) + results = toc() + assert results.micros == (math.floor(results.nanos * pow(10, -3))) - def testMillis(self, t): - t.tic() + def testMillis(self): + tic() print("test") - t.toc() - assert t.results.millis == (math.floor(t.results.nanos * pow(10, -6))) + results = toc() + assert results.millis == (math.floor(results.nanos * pow(10, -6))) - def testSeconds(self, t): - t.tic() + def testSeconds(self): + tic() print("test") - t.toc() - assert t.results.seconds == round( - (t.results.nanos * pow(10, -9)), 9 + results = toc() + assert results.seconds == round( + (results.nanos * pow(10, -9)), 9 ) # f64 vs u128, hence the round diff --git a/tests/testInitSyntax.py b/tests/testInitSyntax.py new file mode 100644 index 0000000..97b7557 --- /dev/null +++ b/tests/testInitSyntax.py @@ -0,0 +1,82 @@ +import pytest +import tictoc +import time +import math + + +@pytest.fixture +def t(): + t = tictoc.init() + return t + + +class testFunctionality: + def testBasic(self, t): + t.tic() + print("test") + t.toc() + assert t.results.seconds > 0 + + def testOverwrite(self, t): + t.tic() + print("test") + t.toc() + firstResult = t.results.seconds + print("test2") + t.toc() + secondResult = t.results.seconds + + assert firstResult < secondResult + + +class testInvalid: + def testNoInit(self): + with pytest.raises(Exception): + t.tic() + + def testTocBeforeTic(self, t): + with pytest.raises(Exception): + t.toc() + + +@pytest.mark.parametrize("sleepTime", [0.05, 0.5, 1]) +class testAccuracy: + @pytest.fixture(scope="class") + def tol(self): + return 0.0006 + + def testSingleCall(self, t, sleepTime, tol): + t.tic() + time.sleep(sleepTime) + t.toc() + assert (t.results.seconds < sleepTime+tol) + + def testMultipleCalls(self, t, sleepTime, tol): + t.tic() + time.sleep(sleepTime) + t.toc() + time.sleep(sleepTime) + t.toc() + assert (t.results.seconds < (sleepTime * 2)+tol) + + +class testConsistency: + def testMicros(self, t): + t.tic() + print("test") + t.toc() + assert t.results.micros == (math.floor(t.results.nanos * pow(10, -3))) + + def testMillis(self, t): + t.tic() + print("test") + t.toc() + assert t.results.millis == (math.floor(t.results.nanos * pow(10, -6))) + + def testSeconds(self, t): + t.tic() + print("test") + t.toc() + assert t.results.seconds == round( + (t.results.nanos * pow(10, -9)), 9 + ) # f64 vs u128, hence the round From 2a3087ad355cdeaa8602743cd6fc707c28b0bbaf Mon Sep 17 00:00:00 2001 From: Andrew Conlin Date: Wed, 18 Dec 2024 13:55:35 +0000 Subject: [PATCH 2/3] [2024-12-18] Update Rust tests, add relevant derives to Results --- src/lib.rs | 44 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 00bbdf3..66a1b23 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,7 +7,7 @@ mod tictoc { use pyo3::exceptions::PyException; #[pyclass] - #[derive(Clone)] + #[derive(Clone, Debug, PartialEq)] struct Results { #[pyo3(get)] nanos: u128, @@ -74,6 +74,7 @@ mod tictoc { } } } + #[test] fn test_new() { let init = Init::new(); @@ -84,17 +85,48 @@ mod tictoc { fn test_tic() { let mut init = Init::new(); let time1 = init.time; - init.tic(); + let _ = init.tic(); let time2 = init.time; assert!(time2 > time1) } - + #[test] fn test_toc() { let mut init = Init::new(); - init.tic(); + let _ = init.tic(); println!("{}","test"); - let _ = init.toc(); - assert!(init.results.nanos > 0) + let _ = init.toc(None).unwrap(); + assert!(init.results.nanos > 0); + } + + #[test] + fn test_passing_tic_to_toc() { + let mut init = Init::new(); + let tic_obj = init.tic().unwrap(); + println!("{}","test"); + let results = init.toc(Some(tic_obj)).unwrap(); + assert!(init.results.nanos > 0); + assert_eq!(init.results,results) + } + + #[test] + fn test_multiple_calls() { + let mut init = Init::new(); + let first_tic = init.tic().unwrap(); + println!("{}","test"); + let second_tic = init.tic().unwrap(); + println!("{}","test"); + let results2 = init.toc(Some(second_tic)).unwrap(); + let results = init.toc(Some(first_tic)).unwrap(); + assert!(results.nanos > results2.nanos); + } + + #[test] + fn test_toc_before_tic() { + let mut init = Init::new(); + //assert!(init.toc(None).is_err()) + pyo3::prepare_freethreaded_python(); + let e = init.toc(None).unwrap_err(); + assert_eq!(e.to_string(),"Exception: tic() must be called before toc()") } } From 5b2a23d06af8943b5e17a325a671fc4058b5c8df Mon Sep 17 00:00:00 2001 From: Andrew Conlin Date: Wed, 18 Dec 2024 15:41:46 +0000 Subject: [PATCH 3/3] [2024-12-18] Update README and run.py to reflect new syntax Also remove GitHub specific links and logo --- README.md | 79 ++++++++++++++++++++++++++++++++++++++----------------- run.py | 25 +++++++++++++----- 2 files changed, 73 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 2a88882..348d3b9 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,5 @@ -![A logo with the word tictoc followed by a stopwatch emoji](./.docs/logoLightMode.png#gh-light-mode-only) -![A logo with the word tictoc followed by a stopwatch emoji](./.docs/logoDarkMode.png#gh-dark-mode-only) - # Fast, simple and accurate Python timing. Written in Rust. -![badge](https://github.com/andrwcnln/tictoc-py/actions/workflows/python.yml/badge.svg) -![badge](https://github.com/andrwcnln/tictoc-py/actions/workflows/rust.yml/badge.svg) ![badge](https://img.shields.io/pypi/dm/tictoc) ## Installation @@ -14,40 +9,76 @@ $ python -m pip install tictoc ``` ## Usage -Import and initialise. **The module must be initialised to be used!** +Import. ```python -import tictoc -t = tictoc.init() +from tictoc import tic,toc ``` -Begin timing with `tic()`, and stop with `toc()`. +If you only want to time one section of code then use `tic() and `toc()` directly. Begin timing with `tic()`, and stop with `toc()`. ```python -t.tic() +tic() # some code -t.toc() +toc() ``` -When `toc` is called, the results are saved. They can be accessed with the following syntax: +A call to `tic()` can be followed with multiple `toc()` calls. Each will print the time elapsed since the most recent `tic()` call. ```python -t.results.{unit} +tic() +time.sleep(3) +toc() +# >>> The elapsed time was 3.000132333 seconds. +time.sleep(3) +toc() +# >>> The elapsed time was 6.000383124 seconds. +``` +For more complex timing operations, you can assign the output of `tic()` and pass it as an input to `toc()`. +> [!NOTE] +> This syntax cannot be used interchangeably with the default syntax above. Any call to `tic()` resets the global timer. +```python +firstTic = tic() +time.sleep(3) +secondTic = tic() +time.sleep(1) +toc(firstTic) +# >>> The elapsed time was 4.000317251 seconds. +time.sleep(3) +toc(secondTic) +# >>> The elapsed time was 4.000312568 seconds. +``` +Any call to `toc()` will print the elapsed time in seconds. You can save the results with full precision by assigning the output of `toc()`. +```python +tic() +# some code +results = toc() ``` The available units are: ```python -t.results.nanos # u128 -t.results.micros # u128 -t.results.millis # u128 -t.results.seconds # f64 +results.nanos # u128 +results.micros # u128 +results.millis # u128 +results.seconds # f64 ``` ## Full example ```python import time +from tictoc import tic,toc -import tictoc -t = tictoc.init() - -t.tic() # start timing +tic() # start timing time.sleep(3) # sleep for 3 seconds -t.toc() # stop timing +toc() # stop timing +# >>> The elapsed time was 3.000132333 seconds. -print(t.results.seconds) -# >>> 3.000457715 +firstTic = tic() +time.sleep(3) +secondTic = tic() +time.sleep(1) +toc(firstTic) +# >>> The elapsed time was 4.000317251 seconds. +time.sleep(3) +toc(secondTic) +# >>> The elapsed time was 4.000312568 seconds. + +tic() +results = toc() +print(results.nanos) +# >>> 2825 ``` diff --git a/run.py b/run.py index 64b2978..62ee3df 100644 --- a/run.py +++ b/run.py @@ -1,11 +1,22 @@ import time +from tictoc import tic,toc -import tictoc +tic() # start timing +time.sleep(3) # sleep for 3 seconds +toc() # stop timing +# >>> The elapsed time was 3.000132333 seconds. -t = tictoc.init() +firstTic = tic() +time.sleep(3) +secondTic = tic() +time.sleep(1) +toc(firstTic) +# >>> The elapsed time was 4.000317251 seconds. +time.sleep(3) +toc(secondTic) +# >>> The elapsed time was 4.000312568 seconds. -t.tic() # start timing -time.sleep(3) # sleep for 3 seconds -t.toc() # stop timing - -print(t.results.seconds) +tic() +results = toc() +print(results.nanos) +# >>> 2825