diff --git a/justfile b/justfile
index de53b89..f0de065 100644
--- a/justfile
+++ b/justfile
@@ -83,13 +83,13 @@ shipit *args:
# Format code with Fantomas
format:
- dotnet fantomas {{src_path}} -r
- dotnet fantomas {{test_path}} -r
+ dotnet fantomas {{src_path}}
+ dotnet fantomas {{test_path}}
# Check code formatting without making changes
format-check:
- dotnet fantomas {{src_path}} -r --check
- dotnet fantomas {{test_path}} -r --check
+ dotnet fantomas {{src_path}} --check
+ dotnet fantomas {{test_path}} --check
# Install .NET tools (Fable, Fantomas) and Python dependencies
setup:
diff --git a/src/Fable.Python.fsproj b/src/Fable.Python.fsproj
index 4920133..3dff498 100644
--- a/src/Fable.Python.fsproj
+++ b/src/Fable.Python.fsproj
@@ -25,6 +25,7 @@
+
diff --git a/src/fable/Testing.fs b/src/fable/Testing.fs
index 70272ae..9f1aa02 100644
--- a/src/fable/Testing.fs
+++ b/src/fable/Testing.fs
@@ -47,7 +47,7 @@ let throwsError (expected: string) (f: unit -> 'a) : unit =
let throwsErrorContaining (expected: string) (f: unit -> 'a) : unit =
match run f with
| Error _ when String.IsNullOrEmpty expected -> ()
- | Error (actual: string) when actual.Contains expected -> ()
+ | Error(actual: string) when actual.Contains expected -> ()
| Error actual -> equal (sprintf "Error containing '%s'" expected) actual
| Ok _ -> equal (sprintf "Error containing '%s'" expected) "No error was thrown"
diff --git a/src/stdlib/Builtins.fs b/src/stdlib/Builtins.fs
index 8260362..f7ee0eb 100644
--- a/src/stdlib/Builtins.fs
+++ b/src/stdlib/Builtins.fs
@@ -353,7 +353,8 @@ let __name__: string = nativeOnly
let print obj = builtins.print obj
/// Return the value of the named attribute of object with a default.
-let getattr obj name defaultValue = builtins.getattr (obj, name, defaultValue)
+let getattr obj name defaultValue =
+ builtins.getattr (obj, name, defaultValue)
/// Sets the named attribute on the given object to the specified value.
let setattr obj name value = builtins.setattr (obj, name, value)
diff --git a/src/stdlib/Datetime.fs b/src/stdlib/Datetime.fs
new file mode 100644
index 0000000..7cc0c42
--- /dev/null
+++ b/src/stdlib/Datetime.fs
@@ -0,0 +1,324 @@
+/// Type bindings for Python datetime module: https://docs.python.org/3/library/datetime.html
+///
+/// Note: this module exposes a `time` class binding for Python's `datetime.time`. If you also
+/// open `Fable.Python.Time` (the `time` module), the `time` identifier will collide — qualify
+/// one of them, e.g. `Fable.Python.Time.time.time()` vs. `Fable.Python.Datetime.time(...)`.
+module Fable.Python.Datetime
+
+open Fable.Core
+
+// fsharplint:disable MemberNames
+
+// ============================================================================
+// timedelta
+// ============================================================================
+
+/// A duration expressing the difference between two date, time, or datetime instances.
+/// See https://docs.python.org/3/library/datetime.html#datetime.timedelta
+///
+/// The empty `timedelta()` ctor creates a zero duration. For other durations, use the
+/// single-unit factories `ofDays`, `ofHours`, `ofMinutes`, `ofSeconds`, `ofWeeks`,
+/// `ofMilliseconds`, `ofMicroseconds`, and combine via `.add` / `.sub`.
+[]
+type timedelta() =
+ /// Number of full days (may be negative)
+ member _.days: int = nativeOnly
+ /// Remaining seconds after full days have been removed; 0 <= seconds < 86400
+ member _.seconds: int = nativeOnly
+ /// Remaining microseconds; 0 <= microseconds < 1000000
+ member _.microseconds: int = nativeOnly
+ /// Return the total duration represented in fractional seconds
+ /// See https://docs.python.org/3/library/datetime.html#datetime.timedelta.total_seconds
+ member _.total_seconds() : float = nativeOnly
+
+ /// Return the sum of two timedeltas
+ []
+ member _.add(other: timedelta) : timedelta = nativeOnly
+
+ /// Return the difference between two timedeltas
+ []
+ member _.sub(other: timedelta) : timedelta = nativeOnly
+
+ /// Return the negation of this timedelta
+ []
+ member _.neg() : timedelta = nativeOnly
+
+ /// The most negative timedelta representable
+ static member min: timedelta = nativeOnly
+ /// The most positive timedelta representable
+ static member max: timedelta = nativeOnly
+ /// The smallest positive difference between non-equal timedelta objects
+ static member resolution: timedelta = nativeOnly
+
+ /// Create a timedelta of N days
+ []
+ static member ofDays(days: float) : timedelta = nativeOnly
+
+ /// Create a timedelta of N seconds
+ []
+ static member ofSeconds(seconds: float) : timedelta = nativeOnly
+
+ /// Create a timedelta of N microseconds
+ []
+ static member ofMicroseconds(microseconds: float) : timedelta = nativeOnly
+
+ /// Create a timedelta of N milliseconds
+ []
+ static member ofMilliseconds(milliseconds: float) : timedelta = nativeOnly
+
+ /// Create a timedelta of N minutes
+ []
+ static member ofMinutes(minutes: float) : timedelta = nativeOnly
+
+ /// Create a timedelta of N hours
+ []
+ static member ofHours(hours: float) : timedelta = nativeOnly
+
+ /// Create a timedelta of N weeks
+ []
+ static member ofWeeks(weeks: float) : timedelta = nativeOnly
+
+// ============================================================================
+// timezone (defined before datetime so datetime members can reference it)
+// ============================================================================
+
+/// A fixed-offset implementation of tzinfo.
+/// See https://docs.python.org/3/library/datetime.html#datetime.timezone
+[]
+type timezone(offset: timedelta, ?name: string) =
+ /// Return the UTC offset for this timezone
+ member _.utcoffset(dt: obj) : timedelta = nativeOnly
+ /// Return the timezone name string for this timezone
+ member _.tzname(dt: obj) : string = nativeOnly
+ /// The UTC timezone singleton (offset zero, name "UTC")
+ static member utc: timezone = nativeOnly
+
+// ============================================================================
+// date
+// ============================================================================
+
+/// A naive date (year, month, day) with no time or timezone component.
+/// See https://docs.python.org/3/library/datetime.html#datetime.date
+[]
+type date(year: int, month: int, day: int) =
+ /// Year in range [MINYEAR, MAXYEAR]
+ member _.year: int = nativeOnly
+ /// Month in range [1, 12]
+ member _.month: int = nativeOnly
+ /// Day in range [1, number of days in the month and year]
+ member _.day: int = nativeOnly
+ /// Return a string in ISO 8601 format, e.g. "2026-04-21"
+ /// See https://docs.python.org/3/library/datetime.html#datetime.date.isoformat
+ member _.isoformat() : string = nativeOnly
+ /// Return a string representing the date, formatted with format
+ /// See https://docs.python.org/3/library/datetime.html#datetime.date.strftime
+ member _.strftime(format: string) : string = nativeOnly
+ /// Return the day of the week as an integer; Monday is 0 and Sunday is 6
+ /// See https://docs.python.org/3/library/datetime.html#datetime.date.weekday
+ member _.weekday() : int = nativeOnly
+ /// Return the day of the week as an integer; Monday is 1 and Sunday is 7
+ /// See https://docs.python.org/3/library/datetime.html#datetime.date.isoweekday
+ member _.isoweekday() : int = nativeOnly
+ /// Return the proleptic Gregorian ordinal of the date; January 1 of year 1 has ordinal 1
+ member _.toordinal() : int = nativeOnly
+
+ /// Return a date with the given fields replaced (any subset of year/month/day)
+ /// See https://docs.python.org/3/library/datetime.html#datetime.date.replace
+ []
+ member _.replace(?year: int, ?month: int, ?day: int) : date = nativeOnly
+
+ /// Return the timedelta between this date and other (self - other)
+ []
+ member _.sub(other: date) : timedelta = nativeOnly
+
+ /// Return a date offset by the given timedelta (self - delta)
+ []
+ member _.sub(delta: timedelta) : date = nativeOnly
+
+ /// Return a date offset by the given timedelta (self + delta)
+ []
+ member _.add(delta: timedelta) : date = nativeOnly
+
+ /// Return the current local date
+ /// See https://docs.python.org/3/library/datetime.html#datetime.date.today
+ static member today() : date = nativeOnly
+ /// Return the local date corresponding to a POSIX timestamp
+ /// See https://docs.python.org/3/library/datetime.html#datetime.date.fromtimestamp
+ static member fromtimestamp(timestamp: float) : date = nativeOnly
+ /// Return the date corresponding to the proleptic Gregorian ordinal
+ static member fromordinal(ordinal: int) : date = nativeOnly
+ /// Return a date from a string in any valid ISO 8601 format
+ /// See https://docs.python.org/3/library/datetime.html#datetime.date.fromisoformat
+ static member fromisoformat(date_string: string) : date = nativeOnly
+ /// The earliest representable date
+ static member min: date = nativeOnly
+ /// The latest representable date
+ static member max: date = nativeOnly
+
+// ============================================================================
+// time
+// ============================================================================
+
+/// A naive or aware time of day (hour, minute, second, microsecond, tzinfo).
+/// See https://docs.python.org/3/library/datetime.html#datetime.time
+///
+/// Ctor args are positional: `time(h)`, `time(h, m)`, `time(h, m, s)`, `time(h, m, s, us)`.
+[]
+type time(hour: int, ?minute: int, ?second: int, ?microsecond: int) =
+ /// Hour in range [0, 23]
+ member _.hour: int = nativeOnly
+ /// Minute in range [0, 59]
+ member _.minute: int = nativeOnly
+ /// Second in range [0, 59]
+ member _.second: int = nativeOnly
+ /// Microsecond in range [0, 999999]
+ member _.microsecond: int = nativeOnly
+ /// Fold value (0 or 1) for disambiguating wall-clock times that repeat during DST transitions
+ member _.fold: int = nativeOnly
+ /// Return a string in ISO 8601 format, e.g. "14:30:00"
+ /// See https://docs.python.org/3/library/datetime.html#datetime.time.isoformat
+ member _.isoformat() : string = nativeOnly
+ /// Return a string representing the time, formatted with format
+ /// See https://docs.python.org/3/library/datetime.html#datetime.time.strftime
+ member _.strftime(format: string) : string = nativeOnly
+ /// Return the UTC offset as a timedelta for aware times; None for naive times
+ member _.utcoffset() : timedelta option = nativeOnly
+ /// Return the timezone abbreviation string for aware times; None for naive times
+ member _.tzname() : string option = nativeOnly
+
+ /// Return a time with the given fields replaced (any subset)
+ /// See https://docs.python.org/3/library/datetime.html#datetime.time.replace
+ []
+ member _.replace(?hour: int, ?minute: int, ?second: int, ?microsecond: int) : time = nativeOnly
+
+ /// Return a time from a string in ISO 8601 format
+ /// See https://docs.python.org/3/library/datetime.html#datetime.time.fromisoformat
+ static member fromisoformat(time_string: string) : time = nativeOnly
+ /// The earliest representable time, time(0, 0, 0, 0)
+ static member min: time = nativeOnly
+ /// The latest representable time, time(23, 59, 59, 999999)
+ static member max: time = nativeOnly
+
+// ============================================================================
+// datetime
+// ============================================================================
+
+/// A naive or aware date and time (year, month, day, hour, minute, second, microsecond, tzinfo).
+/// See https://docs.python.org/3/library/datetime.html#datetime.datetime
+[]
+type datetime
+ (
+ year: int,
+ month: int,
+ day: int,
+ ?hour: int,
+ ?minute: int,
+ ?second: int,
+ ?microsecond: int,
+ ?tzinfo: timezone,
+ ?fold: int
+ ) =
+ /// Year in range [MINYEAR, MAXYEAR]
+ member _.year: int = nativeOnly
+ /// Month in range [1, 12]
+ member _.month: int = nativeOnly
+ /// Day in range [1, number of days in the month and year]
+ member _.day: int = nativeOnly
+ /// Hour in range [0, 23]
+ member _.hour: int = nativeOnly
+ /// Minute in range [0, 59]
+ member _.minute: int = nativeOnly
+ /// Second in range [0, 59]
+ member _.second: int = nativeOnly
+ /// Microsecond in range [0, 999999]
+ member _.microsecond: int = nativeOnly
+ /// Fold value (0 or 1) for disambiguating wall-clock times that repeat during DST transitions
+ member _.fold: int = nativeOnly
+ /// Return the date part as a date object
+ /// See https://docs.python.org/3/library/datetime.html#datetime.datetime.date
+ member _.date() : date = nativeOnly
+ /// Return the time part as a time object (tzinfo is not included)
+ /// See https://docs.python.org/3/library/datetime.html#datetime.datetime.time
+ member _.time() : time = nativeOnly
+ /// Return a string in ISO 8601 format, e.g. "2026-04-21T14:30:00"
+ /// See https://docs.python.org/3/library/datetime.html#datetime.datetime.isoformat
+ member _.isoformat() : string = nativeOnly
+
+ /// Return a string in ISO 8601 format with a custom separator between date and time
+ []
+ member _.isoformat(sep: string) : string = nativeOnly
+
+ /// Return a string representing the datetime, formatted with format
+ /// See https://docs.python.org/3/library/datetime.html#datetime.datetime.strftime
+ member _.strftime(format: string) : string = nativeOnly
+ /// Return the POSIX timestamp corresponding to this datetime as a float
+ /// See https://docs.python.org/3/library/datetime.html#datetime.datetime.timestamp
+ member _.timestamp() : float = nativeOnly
+ /// Return the UTC offset as a timedelta for aware datetimes; None for naive
+ member _.utcoffset() : timedelta option = nativeOnly
+ /// Return the timezone abbreviation string for aware datetimes; None for naive
+ member _.tzname() : string option = nativeOnly
+ /// Return the day of the week as an integer; Monday is 0 and Sunday is 6
+ member _.weekday() : int = nativeOnly
+ /// Return the day of the week as an integer; Monday is 1 and Sunday is 7
+ member _.isoweekday() : int = nativeOnly
+
+ /// Return a datetime with the given fields replaced (any subset)
+ /// See https://docs.python.org/3/library/datetime.html#datetime.datetime.replace
+ []
+ member _.replace
+ (?year: int, ?month: int, ?day: int, ?hour: int, ?minute: int, ?second: int, ?microsecond: int)
+ : datetime =
+ nativeOnly
+
+ /// Return a datetime converted to the given timezone
+ /// See https://docs.python.org/3/library/datetime.html#datetime.datetime.astimezone
+ member _.astimezone(tz: timezone) : datetime = nativeOnly
+
+ /// Return the timedelta between this datetime and other (self - other)
+ []
+ member _.sub(other: datetime) : timedelta = nativeOnly
+
+ /// Return a datetime offset by the given timedelta (self - delta)
+ []
+ member _.sub(delta: timedelta) : datetime = nativeOnly
+
+ /// Return a datetime offset by the given timedelta (self + delta)
+ []
+ member _.add(delta: timedelta) : datetime = nativeOnly
+
+ /// Return the current local date and time
+ /// See https://docs.python.org/3/library/datetime.html#datetime.datetime.now
+ static member now() : datetime = nativeOnly
+
+ /// Return the current local date and time in the given timezone
+ []
+ static member now(tz: timezone) : datetime = nativeOnly
+
+ /// Return the current UTC date and time as a naive datetime.
+ /// Deprecated in Python 3.12; prefer `datetime.now(tz = timezone.utc)`.
+ /// See https://docs.python.org/3/library/datetime.html#datetime.datetime.utcnow
+ []
+ static member utcnow() : datetime = nativeOnly
+
+ /// Return the local datetime corresponding to a POSIX timestamp
+ /// See https://docs.python.org/3/library/datetime.html#datetime.datetime.fromtimestamp
+ static member fromtimestamp(timestamp: float) : datetime = nativeOnly
+
+ /// Return the datetime corresponding to a POSIX timestamp in the given timezone
+ []
+ static member fromtimestamp(timestamp: float, tz: timezone) : datetime = nativeOnly
+
+ /// Return a datetime from a string in any valid ISO 8601 format
+ /// See https://docs.python.org/3/library/datetime.html#datetime.datetime.fromisoformat
+ static member fromisoformat(datetime_string: string) : datetime = nativeOnly
+ /// Return a datetime parsed from date_string according to format
+ /// See https://docs.python.org/3/library/datetime.html#datetime.datetime.strptime
+ static member strptime(date_string: string, format: string) : datetime = nativeOnly
+ /// Combine a date and time into a single datetime
+ /// See https://docs.python.org/3/library/datetime.html#datetime.datetime.combine
+ static member combine(date: date, time: time) : datetime = nativeOnly
+ /// The earliest representable datetime
+ static member min: datetime = nativeOnly
+ /// The latest representable datetime
+ static member max: datetime = nativeOnly
diff --git a/src/stdlib/Math.fs b/src/stdlib/Math.fs
index 07a72e0..c83b28c 100644
--- a/src/stdlib/Math.fs
+++ b/src/stdlib/Math.fs
@@ -84,10 +84,12 @@ type IExports =
/// Return True if the values a and b are close to each other
/// See https://docs.python.org/3/library/math.html#math.isclose
abstract isclose: a: float * b: float -> bool
+
/// Return True if the values a and b are close to each other with custom tolerances
/// See https://docs.python.org/3/library/math.html#math.isclose
[]
abstract isclose: a: float * b: float * ?rel_tol: float * ?abs_tol: float -> bool
+
/// Return the least common multiple of the integers
/// See https://docs.python.org/3/library/math.html#math.lcm
abstract lcm: [] ints: int[] -> int
diff --git a/src/stdlib/Queue.fs b/src/stdlib/Queue.fs
index ca69760..81b7a32 100644
--- a/src/stdlib/Queue.fs
+++ b/src/stdlib/Queue.fs
@@ -34,10 +34,12 @@ type Queue<'T>() =
/// operation goes into an uninterruptible wait on an underlying lock. This means that no exceptions can occur, and
/// in particular a SIGINT will not trigger a KeyboardInterrupt.
member x.get(?block: bool, ?timeout: float) : 'T = nativeOnly
+
/// Equivalent to get(False).
/// See https://docs.python.org/3/library/queue.html#queue.Queue.get_nowait
[]
member x.get_nowait() : 'T = nativeOnly
+
/// Blocks until all items in the queue have been gotten and processed.
///
/// The count of unfinished tasks goes up whenever an item is added to the queue. The count goes down whenever a
@@ -74,6 +76,7 @@ type SimpleQueue<'T>() =
member x.put(item: 'T, ?block: bool, ?timeout: float) : unit = nativeOnly
/// Remove and return an item from the queue.
member x.get(?block: bool, ?timeout: float) : 'T = nativeOnly
+
/// Equivalent to get(False).
[]
member x.get_nowait() : 'T = nativeOnly
diff --git a/test/Fable.Python.Test.fsproj b/test/Fable.Python.Test.fsproj
index f37dca6..441f8a7 100644
--- a/test/Fable.Python.Test.fsproj
+++ b/test/Fable.Python.Test.fsproj
@@ -32,6 +32,7 @@
+
diff --git a/test/TestDatetime.fs b/test/TestDatetime.fs
new file mode 100644
index 0000000..72e0d1b
--- /dev/null
+++ b/test/TestDatetime.fs
@@ -0,0 +1,313 @@
+module Fable.Python.Tests.Datetime
+
+open Fable.Python.Testing
+open Fable.Python.Datetime
+
+// ============================================================================
+// timedelta tests
+// ============================================================================
+
+[]
+let ``test timedelta days property`` () =
+ let td = timedelta.ofDays 3.0
+ td.days |> equal 3
+
+[]
+let ``test timedelta hours to seconds`` () =
+ let td = timedelta.ofHours 2.0
+ td.seconds |> equal 7200
+
+[]
+let ``test timedelta minutes to seconds`` () =
+ let td = timedelta.ofMinutes 90.0
+ td.seconds |> equal 5400
+
+[]
+let ``test timedelta total_seconds`` () =
+ let td = (timedelta.ofDays 1.0).add (timedelta.ofHours 2.0)
+ td.total_seconds () |> equal 93600.0
+
+[]
+let ``test timedelta zero`` () =
+ let td = timedelta ()
+ td.days |> equal 0
+ td.seconds |> equal 0
+ td.microseconds |> equal 0
+
+[]
+let ``test timedelta add`` () =
+ let sum = (timedelta.ofHours 1.0).add (timedelta.ofMinutes 30.0)
+ sum.total_seconds () |> equal 5400.0
+
+[]
+let ``test timedelta sub`` () =
+ let diff = (timedelta.ofHours 2.0).sub (timedelta.ofMinutes 30.0)
+ diff.total_seconds () |> equal 5400.0
+
+[]
+let ``test timedelta neg`` () =
+ let n = (timedelta.ofHours 1.0).neg ()
+ n.total_seconds () |> equal -3600.0
+
+// ============================================================================
+// date tests
+// ============================================================================
+
+[]
+let ``test date year month day properties`` () =
+ let d = date (2026, 4, 21)
+ d.year |> equal 2026
+ d.month |> equal 4
+ d.day |> equal 21
+
+[]
+let ``test date isoformat`` () =
+ let d = date (2026, 4, 21)
+ d.isoformat () |> equal "2026-04-21"
+
+[]
+let ``test date strftime`` () =
+ let d = date (2026, 4, 21)
+ d.strftime ("%Y/%m/%d") |> equal "2026/04/21"
+
+[]
+let ``test date fromisoformat`` () =
+ let d = date.fromisoformat "2026-04-21"
+ d.year |> equal 2026
+ d.month |> equal 4
+ d.day |> equal 21
+
+[]
+let ``test date today returns date`` () =
+ let d = date.today ()
+ d.year > 2024 |> equal true
+
+[]
+let ``test date weekday monday is 0`` () =
+ // 2026-04-20 is a Monday
+ let d = date (2026, 4, 20)
+ d.weekday () |> equal 0
+
+[]
+let ``test date isoweekday monday is 1`` () =
+ // 2026-04-20 is a Monday
+ let d = date (2026, 4, 20)
+ d.isoweekday () |> equal 1
+
+[]
+let ``test date replace all fields`` () =
+ let d = date (2026, 4, 21)
+ let d2 = d.replace (year = 2027, month = 1, day = 15)
+ d2.year |> equal 2027
+ d2.month |> equal 1
+ d2.day |> equal 15
+
+[]
+let ``test date replace single field`` () =
+ let d = date (2026, 4, 21)
+ let d2 = d.replace (year = 2030)
+ d2.year |> equal 2030
+ d2.month |> equal 4
+ d2.day |> equal 21
+
+[]
+let ``test date add timedelta`` () =
+ let d = date (2026, 4, 21)
+ let d2 = d.add (timedelta.ofDays 10.0)
+ d2.day |> equal 1
+ d2.month |> equal 5
+
+[]
+let ``test date sub date returns timedelta`` () =
+ let d1 = date (2026, 4, 21)
+ let d2 = date (2026, 4, 11)
+ let td = d1.sub d2
+ td.days |> equal 10
+
+[]
+let ``test date sub timedelta returns date`` () =
+ let d = date (2026, 4, 21)
+ let d2 = d.sub (timedelta.ofDays 21.0)
+ d2.month |> equal 3
+ d2.day |> equal 31
+
+// ============================================================================
+// time tests
+// ============================================================================
+
+[]
+let ``test time hour minute second properties`` () =
+ let t = time (14, 30, 45)
+ t.hour |> equal 14
+ t.minute |> equal 30
+ t.second |> equal 45
+
+[]
+let ``test time isoformat`` () =
+ let t = time (9, 5, 3)
+ t.isoformat () |> equal "09:05:03"
+
+[]
+let ``test time fromisoformat`` () =
+ let t = time.fromisoformat "14:30:00"
+ t.hour |> equal 14
+ t.minute |> equal 30
+ t.second |> equal 0
+
+[]
+let ``test time defaults to zero`` () =
+ let t = time 0
+ t.hour |> equal 0
+ t.minute |> equal 0
+ t.second |> equal 0
+ t.microsecond |> equal 0
+
+[]
+let ``test time replace single field`` () =
+ let t = time (9, 30, 0)
+ let t2 = t.replace (hour = 14)
+ t2.hour |> equal 14
+ t2.minute |> equal 30
+ t2.second |> equal 0
+
+// ============================================================================
+// datetime tests
+// ============================================================================
+
+[]
+let ``test datetime year month day properties`` () =
+ let dt = datetime (2026, 4, 21)
+ dt.year |> equal 2026
+ dt.month |> equal 4
+ dt.day |> equal 21
+
+[]
+let ``test datetime hour minute second properties`` () =
+ let dt = datetime (2026, 4, 21, 14, 30, 59)
+ dt.hour |> equal 14
+ dt.minute |> equal 30
+ dt.second |> equal 59
+
+[]
+let ``test datetime isoformat`` () =
+ let dt = datetime (2026, 4, 21, 12, 0, 0)
+ dt.isoformat () |> equal "2026-04-21T12:00:00"
+
+[]
+let ``test datetime fromisoformat`` () =
+ let dt = datetime.fromisoformat "2026-04-21T14:30:00"
+ dt.year |> equal 2026
+ dt.month |> equal 4
+ dt.day |> equal 21
+ dt.hour |> equal 14
+ dt.minute |> equal 30
+
+[]
+let ``test datetime now returns current datetime`` () =
+ let dt = datetime.now ()
+ dt.year > 2024 |> equal true
+
+[]
+let ``test datetime now with timezone`` () =
+ let dt = datetime.now (timezone.utc)
+ dt.year > 2024 |> equal true
+ dt.tzname () |> equal (Some "UTC")
+
+[]
+let ``test datetime strptime`` () =
+ let dt = datetime.strptime ("21/04/2026", "%d/%m/%Y")
+ dt.year |> equal 2026
+ dt.month |> equal 4
+ dt.day |> equal 21
+
+[]
+let ``test datetime combine date and time`` () =
+ let d = date (2026, 4, 21)
+ let t = time (10, 0, 0)
+ let dt = datetime.combine (d, t)
+ dt.year |> equal 2026
+ dt.hour |> equal 10
+
+[]
+let ``test datetime date method returns date part`` () =
+ let dt = datetime (2026, 4, 21, 14, 30, 0)
+ let d = dt.date ()
+ d.year |> equal 2026
+ d.month |> equal 4
+ d.day |> equal 21
+
+[]
+let ``test datetime time method returns time part`` () =
+ let dt = datetime (2026, 4, 21, 14, 30, 59)
+ let t = dt.time ()
+ t.hour |> equal 14
+ t.minute |> equal 30
+ t.second |> equal 59
+
+[]
+let ``test datetime replace date fields`` () =
+ let dt = datetime (2026, 4, 21, 12, 0, 0)
+ let dt2 = dt.replace (year = 2027, month = 6, day = 15)
+ dt2.year |> equal 2027
+ dt2.month |> equal 6
+ dt2.day |> equal 15
+ dt2.hour |> equal 12
+
+[]
+let ``test datetime replace time fields`` () =
+ let dt = datetime (2026, 4, 21, 12, 0, 0)
+ let dt2 = dt.replace (hour = 9, minute = 30, second = 0)
+ dt2.hour |> equal 9
+ dt2.minute |> equal 30
+ dt2.year |> equal 2026
+
+[]
+let ``test datetime timestamp roundtrip`` () =
+ let dt1 = datetime.fromisoformat "2026-01-01T00:00:00"
+ let ts = dt1.timestamp ()
+ let dt2 = datetime.fromtimestamp ts
+ dt2.year |> equal dt1.year
+ dt2.month |> equal dt1.month
+ dt2.day |> equal dt1.day
+
+[]
+let ``test datetime add timedelta`` () =
+ let dt = datetime (2026, 4, 21, 12, 0, 0)
+ let dt2 = dt.add (timedelta.ofHours 3.0)
+ dt2.hour |> equal 15
+
+[]
+let ``test datetime sub datetime returns timedelta`` () =
+ let dt1 = datetime (2026, 4, 21, 15, 0, 0)
+ let dt2 = datetime (2026, 4, 21, 12, 0, 0)
+ let td = dt1.sub dt2
+ td.total_seconds () |> equal 10800.0
+
+[]
+let ``test datetime sub timedelta returns datetime`` () =
+ let dt = datetime (2026, 4, 21, 12, 0, 0)
+ let dt2 = dt.sub (timedelta.ofHours 2.0)
+ dt2.hour |> equal 10
+
+// ============================================================================
+// timezone tests
+// ============================================================================
+
+[]
+let ``test timezone utc name`` () =
+ let utcName = timezone.utc.tzname (null)
+ utcName |> equal "UTC"
+
+[]
+let ``test timezone create with offset`` () =
+ let offset = (timedelta.ofHours 5.0).add (timedelta.ofMinutes 30.0)
+ let tz = timezone offset
+ let name = tz.tzname (null)
+ name |> equal "UTC+05:30"
+
+[]
+let ``test timezone create with name`` () =
+ let offset = timedelta.ofHours -5.0
+ let tz = timezone (offset, "EST")
+ let name = tz.tzname (null)
+ name |> equal "EST"
diff --git a/test/TestFastAPI.fs b/test/TestFastAPI.fs
index 4ec54d8..68ca17f 100644
--- a/test/TestFastAPI.fs
+++ b/test/TestFastAPI.fs
@@ -52,14 +52,14 @@ let ``test APIRouter can be created with prefix`` () =
[]
let ``test APIRouter can be created with tags`` () =
- let router = APIRouter(tags = ResizeArray ["users"; "admin"])
+ let router = APIRouter(tags = ResizeArray [ "users"; "admin" ])
notNull router |> equal true
[]
let ``test FastAPI app can include router`` () =
let app = FastAPI()
let router = APIRouter(prefix = "/api")
- app.include_router(router)
+ app.include_router (router)
// If we get here without error, the test passes
true |> equal true
@@ -186,7 +186,7 @@ let ``test Pydantic model works with FastAPI patterns`` () =
[]
let ``test Pydantic model serialization for FastAPI`` () =
let item = Item(Name = "Gadget", Price = 19.99, InStock = false)
- let json = item.model_dump_json()
+ let json = item.model_dump_json ()
json.Contains("Gadget") |> equal true
json.Contains("19.99") |> equal true
@@ -202,50 +202,47 @@ let app = FastAPI(title = "Test API", version = "1.0.0")
[]
type API() =
[]
- static member root() : obj =
- {| message = "Hello World" |}
+ static member root() : obj = {| message = "Hello World" |}
[]
static member get_item(item_id: int) : obj =
- {| item_id = item_id; name = "Test Item" |}
+ {| item_id = item_id
+ name = "Test Item" |}
[]
- static member create_item(item: Item) : obj =
- {| status = "created"; item = item |}
+ static member create_item(item: Item) : obj = {| status = "created"; item = item |}
[]
- static member update_item(item_id: int, item: Item) : obj =
- {| item_id = item_id; item = item |}
+ static member update_item(item_id: int, item: Item) : obj = {| item_id = item_id; item = item |}
[]
- static member delete_item(item_id: int) : obj =
- {| deleted = item_id |}
+ static member delete_item(item_id: int) : obj = {| deleted = item_id |}
[]
let ``test class-based API methods can be called`` () =
- let result = API.root()
+ let result = API.root ()
notNull result |> equal true
[]
let ``test class-based API with path parameter`` () =
- let result = API.get_item(42)
+ let result = API.get_item (42)
notNull result |> equal true
[]
let ``test class-based API POST method`` () =
let item = Item(Name = "Test", Price = 10.0, InStock = true)
- let result = API.create_item(item)
+ let result = API.create_item (item)
notNull result |> equal true
[]
let ``test class-based API PUT method`` () =
let item = Item(Name = "Updated", Price = 20.0, InStock = false)
- let result = API.update_item(1, item)
+ let result = API.update_item (1, item)
notNull result |> equal true
[]
let ``test class-based API DELETE method`` () =
- let result = API.delete_item(1)
+ let result = API.delete_item (1)
notNull result |> equal true
// ============================================================================
@@ -262,47 +259,42 @@ let ``test TestClient can be created`` () =
// Router-based API pattern
// ============================================================================
-let router = APIRouter(prefix = "/users", tags = ResizeArray ["users"])
+let router = APIRouter(prefix = "/users", tags = ResizeArray [ "users" ])
[]
type UsersAPI() =
[]
- static member list_users() : obj =
- {| users = [| "Alice"; "Bob" |] |}
+ static member list_users() : obj = {| users = [| "Alice"; "Bob" |] |}
[]
- static member get_user(user_id: int) : obj =
- {| user_id = user_id |}
+ static member get_user(user_id: int) : obj = {| user_id = user_id |}
[]
- static member create_user(name: string) : obj =
- {| name = name; id = 1 |}
+ static member create_user(name: string) : obj = {| name = name; id = 1 |}
[]
- static member update_user(user_id: int, name: string) : obj =
- {| user_id = user_id; name = name |}
+ static member update_user(user_id: int, name: string) : obj = {| user_id = user_id; name = name |}
[]
- static member delete_user(user_id: int) : obj =
- {| deleted = user_id |}
+ static member delete_user(user_id: int) : obj = {| deleted = user_id |}
[]
let ``test router-based API methods work`` () =
- let users = UsersAPI.list_users()
+ let users = UsersAPI.list_users ()
notNull users |> equal true
[]
let ``test router can be included in app`` () =
let mainApp = FastAPI()
let usersRouter = APIRouter(prefix = "/api/v1")
- mainApp.include_router(usersRouter)
+ mainApp.include_router (usersRouter)
true |> equal true
[]
let ``test router can be included with prefix and tags`` () =
let mainApp = FastAPI()
- let usersRouter = APIRouter(prefix = "/users", tags = ResizeArray ["users"])
- mainApp.include_router_with_prefix_and_tags(usersRouter, "/api/v1", ResizeArray ["api"])
+ let usersRouter = APIRouter(prefix = "/users", tags = ResizeArray [ "users" ])
+ mainApp.include_router_with_prefix_and_tags (usersRouter, "/api/v1", ResizeArray [ "api" ])
true |> equal true
#endif
diff --git a/test/TestFunctools.fs b/test/TestFunctools.fs
index a0f9bb1..40562a1 100644
--- a/test/TestFunctools.fs
+++ b/test/TestFunctools.fs
@@ -5,18 +5,15 @@ open Fable.Python.Functools
[]
let ``test reduce sum works`` () =
- functools.reduce ((fun a b -> a + b), [ 1; 2; 3; 4; 5 ])
- |> equal 15
+ functools.reduce ((fun a b -> a + b), [ 1; 2; 3; 4; 5 ]) |> equal 15
[]
let ``test reduce product works`` () =
- functools.reduce ((fun a b -> a * b), [ 1; 2; 3; 4; 5 ])
- |> equal 120
+ functools.reduce ((fun a b -> a * b), [ 1; 2; 3; 4; 5 ]) |> equal 120
[]
let ``test reduce with initializer works`` () =
- functools.reduce ((fun acc x -> acc + x), [ 1; 2; 3 ], 10)
- |> equal 16
+ functools.reduce ((fun acc x -> acc + x), [ 1; 2; 3 ], 10) |> equal 16
[]
let ``test reduce string fold with initializer works`` () =
@@ -26,9 +23,11 @@ let ``test reduce string fold with initializer works`` () =
[]
let ``test lruCache memoises results`` () =
let callCount = ResizeArray()
+
let expensive (x: int) =
callCount.Add x
x * x
+
let cached = functools.lruCache (128, expensive)
cached 5 |> equal 25
cached 5 |> equal 25
@@ -38,9 +37,11 @@ let ``test lruCache memoises results`` () =
[]
let ``test cache memoises results`` () =
let callCount = ResizeArray()
+
let expensive (x: int) =
callCount.Add x
x * 2
+
let cached = functools.cache expensive
cached 7 |> equal 14
cached 7 |> equal 14
diff --git a/test/TestHeapq.fs b/test/TestHeapq.fs
index 6001186..d0dd22d 100644
--- a/test/TestHeapq.fs
+++ b/test/TestHeapq.fs
@@ -15,13 +15,13 @@ let ``test heappush and heappop work`` () =
[]
let ``test heapify works`` () =
- let heap = ResizeArray [5; 3; 1; 4; 2]
+ let heap = ResizeArray [ 5; 3; 1; 4; 2 ]
heapq.heapify heap
heapq.heappop heap |> equal 1
[]
let ``test heappushpop works`` () =
- let heap = ResizeArray [2; 4; 6]
+ let heap = ResizeArray [ 2; 4; 6 ]
heapq.heapify heap
// Push 1, then pop smallest (1)
heapq.heappushpop (heap, 1) |> equal 1
@@ -30,10 +30,10 @@ let ``test heappushpop works`` () =
[]
let ``test nlargest works`` () =
- let result = heapq.nlargest (3, [1; 5; 2; 8; 3; 7])
- result |> equal (ResizeArray [8; 7; 5])
+ let result = heapq.nlargest (3, [ 1; 5; 2; 8; 3; 7 ])
+ result |> equal (ResizeArray [ 8; 7; 5 ])
[]
let ``test nsmallest works`` () =
- let result = heapq.nsmallest (3, [1; 5; 2; 8; 3; 7])
- result |> equal (ResizeArray [1; 2; 3])
+ let result = heapq.nsmallest (3, [ 1; 5; 2; 8; 3; 7 ])
+ result |> equal (ResizeArray [ 1; 2; 3 ])
diff --git a/test/TestItertools.fs b/test/TestItertools.fs
index 1f9a413..4273d10 100644
--- a/test/TestItertools.fs
+++ b/test/TestItertools.fs
@@ -5,17 +5,11 @@ open Fable.Python.Itertools
[]
let ``test count from start works`` () =
- itertools.count 1
- |> Seq.take 4
- |> Seq.toList
- |> equal [ 1; 2; 3; 4 ]
+ itertools.count 1 |> Seq.take 4 |> Seq.toList |> equal [ 1; 2; 3; 4 ]
[]
let ``test count with step works`` () =
- itertools.count (0, 2)
- |> Seq.take 4
- |> Seq.toList
- |> equal [ 0; 2; 4; 6 ]
+ itertools.count (0, 2) |> Seq.take 4 |> Seq.toList |> equal [ 0; 2; 4; 6 ]
[]
let ``test cycle works`` () =
@@ -26,9 +20,7 @@ let ``test cycle works`` () =
[]
let ``test repeat with times works`` () =
- itertools.repeat ("x", 3)
- |> Seq.toList
- |> equal [ "x"; "x"; "x" ]
+ itertools.repeat ("x", 3) |> Seq.toList |> equal [ "x"; "x"; "x" ]
[]
let ``test accumulate works`` () =
@@ -44,9 +36,7 @@ let ``test accumulate with func works`` () =
[]
let ``test chain two sequences works`` () =
- itertools.chain ([ 1; 2 ], [ 3; 4 ])
- |> Seq.toList
- |> equal [ 1; 2; 3; 4 ]
+ itertools.chain ([ 1; 2 ], [ 3; 4 ]) |> Seq.toList |> equal [ 1; 2; 3; 4 ]
[]
let ``test chain three sequences works`` () =
@@ -80,15 +70,11 @@ let ``test filterfalse works`` () =
[]
let ``test islice with stop works`` () =
- itertools.islice ([ 1; 2; 3; 4; 5 ], 3)
- |> Seq.toList
- |> equal [ 1; 2; 3 ]
+ itertools.islice ([ 1; 2; 3; 4; 5 ], 3) |> Seq.toList |> equal [ 1; 2; 3 ]
[]
let ``test islice with start and stop works`` () =
- itertools.islice ([ 1; 2; 3; 4; 5 ], 1, 4)
- |> Seq.toList
- |> equal [ 2; 3; 4 ]
+ itertools.islice ([ 1; 2; 3; 4; 5 ], 1, 4) |> Seq.toList |> equal [ 2; 3; 4 ]
[]
let ``test pairwise works`` () =
@@ -111,9 +97,7 @@ let ``test combinations works`` () =
[]
let ``test permutations with r works`` () =
- itertools.permutations ([ 1; 2; 3 ], 2)
- |> Seq.length
- |> equal 6
+ itertools.permutations ([ 1; 2; 3 ], 2) |> Seq.length |> equal 6
[]
let ``test product two sequences works`` () =
diff --git a/test/TestJson.fs b/test/TestJson.fs
index 1ba7543..5f2cf5d 100644
--- a/test/TestJson.fs
+++ b/test/TestJson.fs
@@ -66,7 +66,7 @@ let ``test Json.loads with array works`` () =
[]
let ``test Json.dumps with indent works`` () =
let obj = {| A = 1n |}
- let result = Json.dumps(obj, indent = 2)
+ let result = Json.dumps (obj, indent = 2)
result.Contains("\n") |> equal true
[]
diff --git a/test/TestMath.fs b/test/TestMath.fs
index 6141b45..9ff7aea 100644
--- a/test/TestMath.fs
+++ b/test/TestMath.fs
@@ -93,32 +93,25 @@ let ``test pow works`` () =
math.pow (10.0, 2.0) |> equal 100.0
[]
-let ``test sin works`` () =
- math.sin 0.0 |> equal 0.0
+let ``test sin works`` () = math.sin 0.0 |> equal 0.0
[]
-let ``test cos works`` () =
- math.cos 0.0 |> equal 1.0
+let ``test cos works`` () = math.cos 0.0 |> equal 1.0
[]
-let ``test tan works`` () =
- math.tan 0.0 |> equal 0.0
+let ``test tan works`` () = math.tan 0.0 |> equal 0.0
[]
-let ``test asin works`` () =
- math.asin 0.0 |> equal 0.0
+let ``test asin works`` () = math.asin 0.0 |> equal 0.0
[]
-let ``test acos works`` () =
- math.acos 1.0 |> equal 0.0
+let ``test acos works`` () = math.acos 1.0 |> equal 0.0
[]
-let ``test atan works`` () =
- math.atan 0.0 |> equal 0.0
+let ``test atan works`` () = math.atan 0.0 |> equal 0.0
[]
-let ``test atan2 works`` () =
- math.atan2 (0.0, 1.0) |> equal 0.0
+let ``test atan2 works`` () = math.atan2 (0.0, 1.0) |> equal 0.0
[]
let ``test pi constant works`` () =
@@ -133,8 +126,7 @@ let ``test tau constant works`` () =
math.tau |> fun x -> (x > 6.28318 && x < 6.28319) |> equal true
[]
-let ``test inf constant works`` () =
- math.isinf math.inf |> equal true
+let ``test inf constant works`` () = math.isinf math.inf |> equal true
[]
let ``test sqrt works`` () =
@@ -155,8 +147,7 @@ let ``test trunc works`` () =
math.trunc -2.7 |> equal -2
[]
-let ``test hypot works`` () =
- math.hypot (3.0, 4.0) |> equal 5.0
+let ``test hypot works`` () = math.hypot (3.0, 4.0) |> equal 5.0
[]
let ``test isqrt works`` () =
@@ -168,12 +159,10 @@ let ``test fsum works`` () =
math.fsum [ 1.0; 2.0; 3.0 ] |> equal 6.0
[]
-let ``test nan constant works`` () =
- math.isnan math.nan |> equal true
+let ``test nan constant works`` () = math.isnan math.nan |> equal true
[]
-let ``test prod works`` () =
- math.prod [ 1; 2; 3; 4 ] |> equal 24
+let ``test prod works`` () = math.prod [ 1; 2; 3; 4 ] |> equal 24
[]
let ``test perm works`` () =
@@ -185,36 +174,28 @@ let ``test dist works`` () =
math.dist ([| 0.0; 0.0 |], [| 3.0; 4.0 |]) |> equal 5.0
[]
-let ``test cosh works`` () =
- math.cosh 0.0 |> equal 1.0
+let ``test cosh works`` () = math.cosh 0.0 |> equal 1.0
[]
-let ``test sinh works`` () =
- math.sinh 0.0 |> equal 0.0
+let ``test sinh works`` () = math.sinh 0.0 |> equal 0.0
[]
-let ``test tanh works`` () =
- math.tanh 0.0 |> equal 0.0
+let ``test tanh works`` () = math.tanh 0.0 |> equal 0.0
[]
-let ``test acosh works`` () =
- math.acosh 1.0 |> equal 0.0
+let ``test acosh works`` () = math.acosh 1.0 |> equal 0.0
[]
-let ``test asinh works`` () =
- math.asinh 0.0 |> equal 0.0
+let ``test asinh works`` () = math.asinh 0.0 |> equal 0.0
[]
-let ``test atanh works`` () =
- math.atanh 0.0 |> equal 0.0
+let ``test atanh works`` () = math.atanh 0.0 |> equal 0.0
[]
-let ``test erf works`` () =
- math.erf 0.0 |> equal 0.0
+let ``test erf works`` () = math.erf 0.0 |> equal 0.0
[]
-let ``test erfc works`` () =
- math.erfc 0.0 |> equal 1.0
+let ``test erfc works`` () = math.erfc 0.0 |> equal 1.0
[]
let ``test gamma works`` () =
@@ -222,8 +203,7 @@ let ``test gamma works`` () =
math.gamma 5.0 |> equal 24.0
[]
-let ``test lgamma works`` () =
- math.lgamma 1.0 |> equal 0.0
+let ``test lgamma works`` () = math.lgamma 1.0 |> equal 0.0
[]
let ``test log with base works`` () =
@@ -259,8 +239,8 @@ let ``test isclose works`` () =
[]
let ``test isclose with tolerances works`` () =
- math.isclose (1.0, 1.001, rel_tol=0.01) |> equal true
- math.isclose (1.0, 2.0, abs_tol=0.1) |> equal false
+ math.isclose (1.0, 1.001, rel_tol = 0.01) |> equal true
+ math.isclose (1.0, 2.0, abs_tol = 0.1) |> equal false
[]
let ``test nextafter works`` () =
diff --git a/test/TestString.fs b/test/TestString.fs
index 5d1b808..7cb1c29 100644
--- a/test/TestString.fs
+++ b/test/TestString.fs
@@ -17,7 +17,8 @@ let ``test string format 2 works`` () =
[]
let ``test ascii_letters constant`` () =
- pyString.ascii_letters |> equal "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ pyString.ascii_letters
+ |> equal "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
[]
let ``test ascii_lowercase constant`` () =
@@ -28,16 +29,14 @@ let ``test ascii_uppercase constant`` () =
pyString.ascii_uppercase |> equal "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
[]
-let ``test digits constant`` () =
- pyString.digits |> equal "0123456789"
+let ``test digits constant`` () = pyString.digits |> equal "0123456789"
[]
let ``test hexdigits constant`` () =
pyString.hexdigits |> equal "0123456789abcdefABCDEF"
[]
-let ``test octdigits constant`` () =
- pyString.octdigits |> equal "01234567"
+let ``test octdigits constant`` () = pyString.octdigits |> equal "01234567"
[]
let ``test punctuation constant`` () =
diff --git a/test/TestSys.fs b/test/TestSys.fs
index 26b7530..3033dab 100644
--- a/test/TestSys.fs
+++ b/test/TestSys.fs
@@ -4,28 +4,22 @@ open Fable.Python.Testing
open Fable.Python.Sys
[]
-let ``test sys.platform is non-empty string`` () =
- sys.platform.Length > 0 |> equal true
+let ``test sys.platform is non-empty string`` () = sys.platform.Length > 0 |> equal true
[]
-let ``test sys.version is non-empty string`` () =
- sys.version.Length > 0 |> equal true
+let ``test sys.version is non-empty string`` () = sys.version.Length > 0 |> equal true
[]
-let ``test sys.maxsize is positive`` () =
- sys.maxsize > 0n |> equal true
+let ``test sys.maxsize is positive`` () = sys.maxsize > 0n |> equal true
[]
-let ``test sys.maxunicode is 1114111`` () =
- sys.maxunicode |> equal 1114111
+let ``test sys.maxunicode is 1114111`` () = sys.maxunicode |> equal 1114111
[]
-let ``test sys.path has at least one element`` () =
- sys.path.Count > 0 |> equal true
+let ``test sys.path has at least one element`` () = sys.path.Count > 0 |> equal true
[]
-let ``test sys.argv has at least one element`` () =
- sys.argv.Count > 0 |> equal true
+let ``test sys.argv has at least one element`` () = sys.argv.Count > 0 |> equal true
[]
let ``test sys.byteorder is little or big`` () =
diff --git a/test/TestTesting.fs b/test/TestTesting.fs
index cead90a..a2c0750 100644
--- a/test/TestTesting.fs
+++ b/test/TestTesting.fs
@@ -11,7 +11,7 @@ let ``test equal passes for equal values`` () =
equal 1 1
equal "hello" "hello"
equal true true
- equal [1; 2; 3] [1; 2; 3]
+ equal [ 1; 2; 3 ] [ 1; 2; 3 ]
[]
let ``test equal fails for unequal values`` () =
@@ -38,8 +38,7 @@ let ``test notEqual passes for unequal values`` () =
notEqual true false
[]
-let ``test notEqual fails for equal values`` () =
- throwsAnyError (fun () -> notEqual 1 1)
+let ``test notEqual fails for equal values`` () = throwsAnyError (fun () -> notEqual 1 1)
[]
let ``test notEqual fails for equal strings`` () =
@@ -56,23 +55,18 @@ let ``test throwsAnyError passes when function throws`` () =
[]
let ``test throwsAnyError fails when function does not throw`` () =
// Meta-test: throwsAnyError should fail if the function doesn't throw
- throwsAnyError (fun () ->
- throwsAnyError (fun () -> 42)
- )
+ throwsAnyError (fun () -> throwsAnyError (fun () -> 42))
// ============================================================================
// Test doesntThrow
// ============================================================================
[]
-let ``test doesntThrow passes when function succeeds`` () =
- doesntThrow (fun () -> 1 + 1)
+let ``test doesntThrow passes when function succeeds`` () = doesntThrow (fun () -> 1 + 1)
[]
let ``test doesntThrow fails when function throws`` () =
- throwsAnyError (fun () ->
- doesntThrow (fun () -> failwith "boom")
- )
+ throwsAnyError (fun () -> doesntThrow (fun () -> failwith "boom"))
// ============================================================================
// Test throwsError with exact message
@@ -84,15 +78,11 @@ let ``test throwsError passes with matching message`` () =
[]
let ``test throwsError fails with wrong message`` () =
- throwsAnyError (fun () ->
- throwsError "expected message" (fun () -> failwith "different message")
- )
+ throwsAnyError (fun () -> throwsError "expected message" (fun () -> failwith "different message"))
[]
let ``test throwsError fails when no error thrown`` () =
- throwsAnyError (fun () ->
- throwsError "expected error" (fun () -> 42)
- )
+ throwsAnyError (fun () -> throwsError "expected error" (fun () -> 42))
// ============================================================================
// Test throwsErrorContaining
@@ -104,12 +94,8 @@ let ``test throwsErrorContaining passes when message contains substring`` () =
[]
let ``test throwsErrorContaining fails when message does not contain substring`` () =
- throwsAnyError (fun () ->
- throwsErrorContaining "notfound" (fun () -> failwith "different error message")
- )
+ throwsAnyError (fun () -> throwsErrorContaining "notfound" (fun () -> failwith "different error message"))
[]
let ``test throwsErrorContaining fails when no error thrown`` () =
- throwsAnyError (fun () ->
- throwsErrorContaining "error" (fun () -> 42)
- )
+ throwsAnyError (fun () -> throwsErrorContaining "error" (fun () -> 42))
diff --git a/test/TestTime.fs b/test/TestTime.fs
index e1dba1a..fb9ba4c 100644
--- a/test/TestTime.fs
+++ b/test/TestTime.fs
@@ -40,8 +40,7 @@ let ``test time.ctime with seconds returns non-empty string`` () =
s.Length > 0 |> equal true
[]
-let ``test time.sleep does not throw`` () =
- time.sleep 0.0
+let ``test time.sleep does not throw`` () = time.sleep 0.0
[]
let ``test time.timezone is int`` () =