diff --git a/CHANGELOG.md b/CHANGELOG.md
index 53626ba..f0da69e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,19 @@ All notable changes to this project are documented here. The format is based on
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres
to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [Unreleased]
+
+### Added
+- Full PHPUnit test suite alongside the `.phpt` tests; **100% line coverage**.
+
+### Changed
+- Default unit registration moved from the autoload shim into a testable
+ `DefaultUnits::register()` class.
+
+### Fixed
+- `humanize()` no longer throws when the registry is only partially populated;
+ it now skips ladder units that are not registered.
+
## [1.2.0] - 2026-07-10
### Added
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 038b91f..0815244 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -3,11 +3,16 @@
tests
+ tests
src
+
+
+ src/Unit/bootstrap.php
+
diff --git a/src/Unit/DefaultUnits.php b/src/Unit/DefaultUnits.php
new file mode 100644
index 0000000..3d0e050
--- /dev/null
+++ b/src/Unit/DefaultUnits.php
@@ -0,0 +1,136 @@
+label(), $name, $factor, $dimension, $offset));
+ }
+
+ public static function register(): void
+ {
+ $length = new Dimension(['L' => 1]);
+ $mass = new Dimension(['M' => 1]);
+ $time = new Dimension(['T' => 1]);
+ $area = new Dimension(['L' => 2]);
+ $volume = new Dimension(['L' => 3]);
+ $temperature = new Dimension(['Θ' => 1]);
+ $speed = new Dimension(['L' => 1, 'T' => -1]);
+ $data = new Dimension(['B' => 1]);
+ $force = new Dimension(['M' => 1, 'L' => 1, 'T' => -2]);
+ $energy = new Dimension(['M' => 1, 'L' => 2, 'T' => -2]);
+ $power = new Dimension(['M' => 1, 'L' => 2, 'T' => -3]);
+ $pressure = new Dimension(['M' => 1, 'L' => -1, 'T' => -2]);
+ $frequency = new Dimension(['T' => -1]);
+ $angle = new Dimension(['A' => 1]);
+
+ // Length (base unit: metre)
+ self::add(Name::MM, 0.001, $length);
+ self::add(Name::CM, 0.01, $length);
+ self::add(Name::M, 1.0, $length);
+ self::add(Name::KM, 1000.0, $length);
+ self::add(Name::INCH, 0.0254, $length);
+ self::add(Name::FT, 0.3048, $length);
+ self::add(Name::YD, 0.9144, $length);
+ self::add(Name::MI, 1609.344, $length);
+
+ // Mass (base unit: kilogram)
+ self::add(Name::MG, 1e-6, $mass);
+ self::add(Name::G, 0.001, $mass);
+ self::add(Name::KG, 1.0, $mass);
+ self::add(Name::TON, 1000.0, $mass);
+ self::add(Name::LB, 0.45359237, $mass);
+ self::add(Name::OZ, 0.028349523125, $mass);
+
+ // Time (base unit: second)
+ self::add(Name::MS, 0.001, $time);
+ self::add(Name::S, 1.0, $time);
+ self::add(Name::MIN, 60.0, $time);
+ self::add(Name::H, 3600.0, $time);
+ self::add(Name::DAY, 86400.0, $time);
+ self::add(Name::WK, 604800.0, $time);
+
+ // Area (base unit: square metre)
+ self::add(Name::CM2, 0.0001, $area);
+ self::add(Name::M2, 1.0, $area);
+ self::add(Name::KM2, 1e6, $area);
+ self::add(Name::FT2, 0.09290304, $area);
+ self::add(Name::HA, 10000.0, $area);
+ self::add(Name::ACRE, 4046.8564224, $area);
+
+ // Volume (base unit: cubic metre)
+ self::add(Name::ML, 1e-6, $volume);
+ self::add(Name::L, 0.001, $volume);
+ self::add(Name::M3, 1.0, $volume);
+ self::add(Name::GAL, 0.003785411784, $volume);
+
+ // Temperature (base unit: kelvin) — affine scales use factor + offset.
+ self::add(Name::K, 1.0, $temperature, 0.0);
+ self::add(Name::C, 1.0, $temperature, 273.15);
+ self::add(Name::F, 5.0 / 9.0, $temperature, 273.15 - (32.0 * 5.0 / 9.0));
+
+ // Speed (base unit: metre per second)
+ self::add(Name::MPS, 1.0, $speed);
+ self::add(Name::KMH, 1000.0 / 3600.0, $speed);
+ self::add(Name::MPH, 0.44704, $speed);
+ self::add(Name::KNOT, 1852.0 / 3600.0, $speed);
+
+ // Data (base unit: byte)
+ self::add(Name::BIT, 0.125, $data);
+ self::add(Name::BYTE, 1.0, $data);
+ self::add(Name::KB, 1e3, $data);
+ self::add(Name::MB, 1e6, $data);
+ self::add(Name::GB, 1e9, $data);
+ self::add(Name::TB, 1e12, $data);
+ self::add(Name::KIB, 1024.0, $data);
+ self::add(Name::MIB, 1024.0 ** 2, $data);
+ self::add(Name::GIB, 1024.0 ** 3, $data);
+ self::add(Name::TIB, 1024.0 ** 4, $data);
+
+ // Force (base unit: newton)
+ self::add(Name::N, 1.0, $force);
+ self::add(Name::KN, 1000.0, $force);
+
+ // Energy (base unit: joule)
+ self::add(Name::J, 1.0, $energy);
+ self::add(Name::KJ, 1000.0, $energy);
+ self::add(Name::CAL, 4.184, $energy);
+ self::add(Name::KCAL, 4184.0, $energy);
+ self::add(Name::WH, 3600.0, $energy);
+ self::add(Name::KWH, 3.6e6, $energy);
+
+ // Power (base unit: watt)
+ self::add(Name::W, 1.0, $power);
+ self::add(Name::KW, 1000.0, $power);
+ self::add(Name::HP, 745.6998715822702, $power);
+
+ // Pressure (base unit: pascal)
+ self::add(Name::PA, 1.0, $pressure);
+ self::add(Name::KPA, 1000.0, $pressure);
+ self::add(Name::BAR, 1e5, $pressure);
+ self::add(Name::ATM, 101325.0, $pressure);
+ self::add(Name::PSI, 6894.757293168361, $pressure);
+
+ // Frequency (base unit: hertz)
+ self::add(Name::HZ, 1.0, $frequency);
+ self::add(Name::KHZ, 1e3, $frequency);
+ self::add(Name::MHZ, 1e6, $frequency);
+ self::add(Name::GHZ, 1e9, $frequency);
+
+ // Angle (base unit: radian)
+ self::add(Name::RAD, 1.0, $angle);
+ self::add(Name::DEG, M_PI / 180.0, $angle);
+ self::add(Name::GRAD, M_PI / 200.0, $angle);
+ self::add(Name::TURN, 2.0 * M_PI, $angle);
+ }
+}
diff --git a/src/Unit/Quantity.php b/src/Unit/Quantity.php
index e99446d..9df5a05 100644
--- a/src/Unit/Quantity.php
+++ b/src/Unit/Quantity.php
@@ -135,19 +135,19 @@ public function humanize(): self
return $this;
}
+ $base = $this->toBase();
foreach (self::humanizeLadders() as $symbols) {
- if (!UnitRegistry::has($symbols[0])) {
+ $registered = array_values(array_filter($symbols, UnitRegistry::has(...)));
+ if ($registered === []) {
continue;
}
- if (!UnitRegistry::get($symbols[0])->dimension->equals($this->unit->dimension)) {
+ if (!UnitRegistry::get($registered[0])->dimension->equals($this->unit->dimension)) {
continue;
}
- $base = $this->toBase();
- $chosen = $symbols[0];
- foreach ($symbols as $symbol) {
- $candidate = UnitRegistry::get($symbol);
- if (abs($base / $candidate->factor) >= 1.0) {
+ $chosen = $registered[0];
+ foreach ($registered as $symbol) {
+ if (abs($base / UnitRegistry::get($symbol)->factor) >= 1.0) {
$chosen = $symbol;
} else {
break;
diff --git a/src/Unit/bootstrap.php b/src/Unit/bootstrap.php
index 6f2f7c4..eeac815 100644
--- a/src/Unit/bootstrap.php
+++ b/src/Unit/bootstrap.php
@@ -1,132 +1,16 @@
label(), $name, $factor, $dimension, $offset));
-};
-
-$length = new Dimension(['L' => 1]);
-$mass = new Dimension(['M' => 1]);
-$time = new Dimension(['T' => 1]);
-$area = new Dimension(['L' => 2]);
-$volume = new Dimension(['L' => 3]);
-$temperature = new Dimension(['Θ' => 1]);
-$speed = new Dimension(['L' => 1, 'T' => -1]);
-$data = new Dimension(['B' => 1]);
-$force = new Dimension(['M' => 1, 'L' => 1, 'T' => -2]);
-$energy = new Dimension(['M' => 1, 'L' => 2, 'T' => -2]);
-$power = new Dimension(['M' => 1, 'L' => 2, 'T' => -3]);
-$pressure = new Dimension(['M' => 1, 'L' => -1, 'T' => -2]);
-$frequency = new Dimension(['T' => -1]);
-$angle = new Dimension(['A' => 1]);
-
-// Length (base unit: metre)
-$register(Name::MM, 0.001, $length);
-$register(Name::CM, 0.01, $length);
-$register(Name::M, 1.0, $length);
-$register(Name::KM, 1000.0, $length);
-$register(Name::INCH, 0.0254, $length);
-$register(Name::FT, 0.3048, $length);
-$register(Name::YD, 0.9144, $length);
-$register(Name::MI, 1609.344, $length);
-
-// Mass (base unit: kilogram)
-$register(Name::MG, 1e-6, $mass);
-$register(Name::G, 0.001, $mass);
-$register(Name::KG, 1.0, $mass);
-$register(Name::TON, 1000.0, $mass);
-$register(Name::LB, 0.45359237, $mass);
-$register(Name::OZ, 0.028349523125, $mass);
-
-// Time (base unit: second)
-$register(Name::MS, 0.001, $time);
-$register(Name::S, 1.0, $time);
-$register(Name::MIN, 60.0, $time);
-$register(Name::H, 3600.0, $time);
-$register(Name::DAY, 86400.0, $time);
-$register(Name::WK, 604800.0, $time);
-
-// Area (base unit: square metre)
-$register(Name::CM2, 0.0001, $area);
-$register(Name::M2, 1.0, $area);
-$register(Name::KM2, 1e6, $area);
-$register(Name::FT2, 0.09290304, $area);
-$register(Name::HA, 10000.0, $area);
-$register(Name::ACRE, 4046.8564224, $area);
-
-// Volume (base unit: cubic metre)
-$register(Name::ML, 1e-6, $volume);
-$register(Name::L, 0.001, $volume);
-$register(Name::M3, 1.0, $volume);
-$register(Name::GAL, 0.003785411784, $volume);
-
-// Temperature (base unit: kelvin) — affine scales use factor + offset.
-$register(Name::K, 1.0, $temperature, 0.0);
-$register(Name::C, 1.0, $temperature, 273.15);
-$register(Name::F, 5.0 / 9.0, $temperature, 273.15 - (32.0 * 5.0 / 9.0));
-
-// Speed (base unit: metre per second)
-$register(Name::MPS, 1.0, $speed);
-$register(Name::KMH, 1000.0 / 3600.0, $speed);
-$register(Name::MPH, 0.44704, $speed);
-$register(Name::KNOT, 1852.0 / 3600.0, $speed);
-
-// Data (base unit: byte)
-$register(Name::BIT, 0.125, $data);
-$register(Name::BYTE, 1.0, $data);
-$register(Name::KB, 1e3, $data);
-$register(Name::MB, 1e6, $data);
-$register(Name::GB, 1e9, $data);
-$register(Name::TB, 1e12, $data);
-$register(Name::KIB, 1024.0, $data);
-$register(Name::MIB, 1024.0 ** 2, $data);
-$register(Name::GIB, 1024.0 ** 3, $data);
-$register(Name::TIB, 1024.0 ** 4, $data);
-
-// Force (base unit: newton)
-$register(Name::N, 1.0, $force);
-$register(Name::KN, 1000.0, $force);
-
-// Energy (base unit: joule)
-$register(Name::J, 1.0, $energy);
-$register(Name::KJ, 1000.0, $energy);
-$register(Name::CAL, 4.184, $energy);
-$register(Name::KCAL, 4184.0, $energy);
-$register(Name::WH, 3600.0, $energy);
-$register(Name::KWH, 3.6e6, $energy);
-
-// Power (base unit: watt)
-$register(Name::W, 1.0, $power);
-$register(Name::KW, 1000.0, $power);
-$register(Name::HP, 745.6998715822702, $power);
-
-// Pressure (base unit: pascal)
-$register(Name::PA, 1.0, $pressure);
-$register(Name::KPA, 1000.0, $pressure);
-$register(Name::BAR, 1e5, $pressure);
-$register(Name::ATM, 101325.0, $pressure);
-$register(Name::PSI, 6894.757293168361, $pressure);
-
-// Frequency (base unit: hertz)
-$register(Name::HZ, 1.0, $frequency);
-$register(Name::KHZ, 1e3, $frequency);
-$register(Name::MHZ, 1e6, $frequency);
-$register(Name::GHZ, 1e9, $frequency);
+use KhaledAlam\Unit\DefaultUnits;
-// Angle (base unit: radian)
-$register(Name::RAD, 1.0, $angle);
-$register(Name::DEG, M_PI / 180.0, $angle);
-$register(Name::GRAD, M_PI / 200.0, $angle);
-$register(Name::TURN, 2.0 * M_PI, $angle);
+DefaultUnits::register();
diff --git a/tests/DefaultUnitsTest.php b/tests/DefaultUnitsTest.php
new file mode 100644
index 0000000..3f6ea9a
--- /dev/null
+++ b/tests/DefaultUnitsTest.php
@@ -0,0 +1,63 @@
+assertTrue(
+ UnitRegistry::has($case->value),
+ "Unit {$case->value} was not registered"
+ );
+ }
+
+ $this->assertCount(count(Name::cases()), UnitRegistry::all());
+ }
+
+ /**
+ * @return array
+ */
+ public static function conversions(): array
+ {
+ return [
+ 'km/h -> mph' => [100.0, 'km/h', 'mph', 62.137119, 6],
+ 'GiB -> MiB' => [1.0, 'GiB', 'MiB', 1024.0, 6],
+ 'kWh -> J' => [1.0, 'kWh', 'J', 3_600_000.0, 6],
+ 'mi -> km' => [1.0, 'mi', 'km', 1.609344, 6],
+ 'atm -> psi' => [1.0, 'atm', 'psi', 14.695949, 6],
+ 'deg -> rad' => [180.0, 'deg', 'rad', 3.141593, 6],
+ 'hp -> W' => [1.0, 'hp', 'W', 745.699872, 6],
+ 'lb -> g' => [1.0, 'lb', 'g', 453.59237, 6],
+ 'acre -> m²' => [1.0, 'acre', 'm²', 4046.856422, 6],
+ 'gal -> L' => [1.0, 'gal', 'L', 3.785412, 6],
+ ];
+ }
+
+ #[DataProvider('conversions')]
+ public function test_conversions_are_accurate(
+ float $value,
+ string $from,
+ string $to,
+ float $expected,
+ int $precision
+ ): void {
+ $result = Quantity::of($value, $from)->convertTo($to)->getValue();
+ $this->assertEqualsWithDelta($expected, $result, 10 ** -$precision);
+ }
+}
diff --git a/tests/DimensionTest.php b/tests/DimensionTest.php
new file mode 100644
index 0000000..10f63a7
--- /dev/null
+++ b/tests/DimensionTest.php
@@ -0,0 +1,48 @@
+ 0, 'L' => 1]);
+ $this->assertSame(['L' => 1], $d->exponents);
+ $this->assertFalse($d->isDimensionless());
+ }
+
+ public function test_multiply_and_divide(): void
+ {
+ $length = new Dimension(['L' => 1]);
+ $time = new Dimension(['T' => 1]);
+
+ $speed = $length->divide($time);
+ $this->assertSame(['L' => 1, 'T' => -1], $speed->exponents);
+
+ $area = $length->multiply($length);
+ $this->assertSame(['L' => 2], $area->exponents);
+ }
+
+ public function test_division_by_same_dimension_is_dimensionless(): void
+ {
+ $length = new Dimension(['L' => 1]);
+ $cancelled = $length->divide($length);
+
+ $this->assertTrue($cancelled->isDimensionless());
+ $this->assertTrue($cancelled->equals(new Dimension()));
+ }
+
+ public function test_equals_and_to_string(): void
+ {
+ $a = new Dimension(['L' => 1, 'T' => -1]);
+ $b = new Dimension(['T' => -1, 'L' => 1]);
+
+ $this->assertTrue($a->equals($b));
+ $this->assertFalse($a->equals(new Dimension(['L' => 1])));
+ $this->assertSame('{"L":1,"T":-1}', (string) $a);
+ $this->assertSame('[]', (string) new Dimension());
+ }
+}
diff --git a/tests/LaravelIntegrationTest.php b/tests/LaravelIntegrationTest.php
new file mode 100644
index 0000000..9538c27
--- /dev/null
+++ b/tests/LaravelIntegrationTest.php
@@ -0,0 +1,91 @@
+markTestSkipped('illuminate packages are not installed.');
+ }
+ }
+
+ public function test_service_provider_registers_custom_units_from_config(): void
+ {
+ $furlong = new Unit('furlong', 'fur', 201.168, new Dimension(['L' => 1]));
+
+ $container = new Container();
+ $container->instance('config', new class ($furlong) {
+ /** @param Unit $furlong */
+ public function __construct(private $furlong)
+ {
+ }
+
+ public function get(string $key, mixed $default = null): mixed
+ {
+ return $key === 'unit.units' ? [$this->furlong, 'not-a-unit'] : $default;
+ }
+ });
+
+ (new UnitServiceProvider($container))->boot();
+
+ $this->assertTrue(UnitRegistry::has('fur'));
+ $this->assertEqualsWithDelta(
+ 201.168,
+ Quantity::of(1, 'fur')->convertTo('m')->getValue(),
+ 1e-9
+ );
+ }
+
+ public function test_cast_round_trips_a_quantity(): void
+ {
+ $cast = new AsQuantity();
+ $model = new class extends Model {
+ };
+
+ // set() from a Quantity
+ $stored = $cast->set($model, 'distance', Quantity::of(2.5, 'km'), []);
+ $this->assertSame(['distance' => '2.5 km'], $stored);
+
+ // set() from a string (parsed + normalized)
+ $fromString = $cast->set($model, 'distance', '5 ft 3 in', []);
+ $this->assertSame(['distance' => '5.25 ft'], $fromString);
+
+ // set() null
+ $this->assertSame(['distance' => null], $cast->set($model, 'distance', null, []));
+
+ // get() hydrates a Quantity
+ $quantity = $cast->get($model, 'distance', '2.5 km', []);
+ $this->assertInstanceOf(Quantity::class, $quantity);
+ $this->assertSame('2500 m', (string) $quantity->to('m'));
+
+ // get() null / empty
+ $this->assertNull($cast->get($model, 'distance', null, []));
+ $this->assertNull($cast->get($model, 'distance', '', []));
+
+ // get() passes through an existing Quantity
+ $existing = Quantity::of(1, 'm');
+ $this->assertSame($existing, $cast->get($model, 'distance', $existing, []));
+ }
+
+ public function test_cast_set_rejects_invalid_type(): void
+ {
+ $cast = new AsQuantity();
+ $model = new class extends Model {
+ };
+
+ $this->expectException(\InvalidArgumentException::class);
+ $cast->set($model, 'distance', 123, []);
+ }
+}
diff --git a/tests/NameTest.php b/tests/NameTest.php
new file mode 100644
index 0000000..487cae4
--- /dev/null
+++ b/tests/NameTest.php
@@ -0,0 +1,35 @@
+label();
+ $this->assertNotSame('', $label, "Missing label for {$case->value}");
+ $this->assertMatchesRegularExpression('/^[A-Z]/', $label);
+ }
+ }
+
+ public function test_labels_are_unique(): void
+ {
+ $labels = array_map(static fn (Name $n): string => $n->label(), Name::cases());
+ $this->assertSame(count($labels), count(array_unique($labels)));
+ }
+
+ public function test_specific_labels(): void
+ {
+ $this->assertSame('Meter', Name::M->label());
+ $this->assertSame('Kilometer per hour', Name::KMH->label());
+ $this->assertSame('Gibibyte', Name::GIB->label());
+ $this->assertSame('Pound per square inch', Name::PSI->label());
+ }
+}
diff --git a/tests/QuantityEdgeTest.php b/tests/QuantityEdgeTest.php
new file mode 100644
index 0000000..61eacee
--- /dev/null
+++ b/tests/QuantityEdgeTest.php
@@ -0,0 +1,87 @@
+expectException(\InvalidArgumentException::class);
+ Quantity::of(1, 'm', -1);
+ }
+
+ public function test_subtract_across_units(): void
+ {
+ $result = Quantity::of(2, 'm')->subtract(Quantity::of(50, 'cm'));
+ $this->assertEqualsWithDelta(1.5, $result->getValue(), 1e-9);
+ }
+
+ public function test_divide_by_zero_quantity_throws(): void
+ {
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Cannot divide by a zero quantity.');
+ Quantity::of(10, 'm')->divide(Quantity::of(0, 's'));
+ }
+
+ public function test_division_of_same_unit_cancels_symbol(): void
+ {
+ $ratio = Quantity::of(10, 'm')->divide(Quantity::of(2, 'm'));
+ $this->assertSame(5.0, $ratio->getValue());
+ $this->assertSame('', $ratio->getUnit()->symbolString());
+ $this->assertTrue($ratio->getUnit()->dimension->isDimensionless());
+ }
+
+ public function test_multiply_same_unit_squares_symbol(): void
+ {
+ $area = Quantity::of(2, 'm')->multiply(Quantity::of(3, 'm'));
+ $this->assertSame('m²', $area->getUnit()->symbolString());
+ }
+
+ public function test_comparison_helpers(): void
+ {
+ $this->assertTrue(Quantity::of(1, 'm')->isLessThan(Quantity::of(200, 'cm')));
+ $this->assertFalse(Quantity::of(1, 'm')->isLessThan(Quantity::of(50, 'cm')));
+ $this->assertFalse(Quantity::of(1, 'm')->equals(Quantity::of(3, 's')));
+ }
+
+ public function test_humanize_returns_self_when_no_ladder_applies(): void
+ {
+ // Frequency has no humanize ladder — value/unit stay unchanged.
+ $freq = Quantity::of(2, 'kHz');
+ $this->assertSame('2 kHz', (string) $freq->humanize());
+ }
+
+ public function test_humanize_skips_ladders_whose_units_are_not_registered(): void
+ {
+ // Only data units registered: humanize must skip the length/mass/etc.
+ // ladders (their base symbols are absent) and still find the data ladder.
+ UnitRegistry::clear();
+ $data = new Dimension(['B' => 1]);
+ UnitRegistry::register(new Unit('Byte', 'B', 1.0, $data));
+ UnitRegistry::register(new Unit('Kilobyte', 'KB', 1e3, $data));
+ UnitRegistry::register(new Unit('Megabyte', 'MB', 1e6, $data));
+
+ $this->assertSame('2.5 MB', (string) Quantity::of(2_500_000, 'B')->humanize());
+ }
+
+ public function test_withPrecision_and_json(): void
+ {
+ $q = Quantity::of(1.23456, 'm')->withPrecision(2);
+ $this->assertSame('1.23 m', (string) $q);
+ $this->assertSame('{"value":1.23456,"unit":"m"}', json_encode($q));
+ }
+}
diff --git a/tests/UnitRegistryTest.php b/tests/UnitRegistryTest.php
new file mode 100644
index 0000000..c2cd579
--- /dev/null
+++ b/tests/UnitRegistryTest.php
@@ -0,0 +1,48 @@
+assertFalse(UnitRegistry::has('yd'));
+
+ $yard = new Unit('yard', 'yd', 0.9144, new Dimension(['L' => 1]));
+ UnitRegistry::register($yard);
+
+ $this->assertTrue(UnitRegistry::has('yd'));
+ $this->assertSame($yard, UnitRegistry::get('yd'));
+ $this->assertSame(['yd' => $yard], UnitRegistry::all());
+ }
+
+ public function test_get_unknown_unit_throws(): void
+ {
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Unknown unit: nope');
+ UnitRegistry::get('nope');
+ }
+
+ public function test_clear_empties_the_registry(): void
+ {
+ DefaultUnits::register();
+ $this->assertNotEmpty(UnitRegistry::all());
+
+ UnitRegistry::clear();
+ $this->assertEmpty(UnitRegistry::all());
+ }
+}
diff --git a/tests/UnitTest.php b/tests/UnitTest.php
new file mode 100644
index 0000000..7dfb719
--- /dev/null
+++ b/tests/UnitTest.php
@@ -0,0 +1,34 @@
+ 1]));
+
+ $this->assertSame('m', $unit->symbolString());
+ $this->assertSame('m', (string) $unit);
+ $this->assertSame(0.0, $unit->offset);
+ }
+
+ public function test_string_symbol_does_not_error(): void
+ {
+ $derived = new Unit('derived', 'm/s', 1.0, new Dimension(['L' => 1, 'T' => -1]));
+
+ $this->assertSame('m/s', $derived->symbolString());
+ $this->assertSame('m/s', (string) $derived);
+ }
+
+ public function test_offset_is_retained(): void
+ {
+ $celsius = new Unit('Celsius', Name::C, 1.0, new Dimension(['Θ' => 1]), 273.15);
+ $this->assertSame(273.15, $celsius->offset);
+ }
+}
diff --git a/tests/laravel/cast.phpt b/tests/laravel/cast.phpt
deleted file mode 100644
index 40eff3d..0000000
--- a/tests/laravel/cast.phpt
+++ /dev/null
@@ -1,42 +0,0 @@
---TEST--
-Laravel AsQuantity Eloquent cast round-trips quantities
---SKIPIF--
-
---FILE--
- AsQuantity::class];
- }
-};
-
-$model->distance = Quantity::of(2.5, 'km');
-echo $model->getAttributes()['distance'] . "\n"; // stored string
-echo $model->distance->to('m') . "\n"; // hydrated + converted
-
-$model->distance = '5 ft 3 in';
-echo $model->getAttributes()['distance'] . "\n"; // parsed + stored
-echo $model->distance->humanize()->format(4) . "\n"; // hydrated + humanized
-
-$model->distance = null;
-var_dump($model->distance);
-?>
---EXPECT--
-2.5 km
-2500 m
-5.25 ft
-1.6002 m
-NULL