@quenty/metricutils 2.4.3 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -54
- package/README.md +80 -2
- package/deploy.nevermore.json +10 -0
- package/package.json +22 -4
- package/src/Shared/Definitions/MetricUnitDefinitions.lua +1082 -0
- package/src/Shared/Definitions/MetricUnitDefinitions.spec.lua +55 -0
- package/src/Shared/Definitions/MetricUnitDimensionUtils.lua +164 -0
- package/src/Shared/Definitions/MetricUnitDimensionUtils.spec.lua +49 -0
- package/src/Shared/Definitions/MetricUnitKinds.lua +237 -0
- package/src/Shared/Definitions/MetricUnitKinds.spec.lua +51 -0
- package/src/Shared/Formatting/MetricUnitDisplayUnitUtils.lua +129 -0
- package/src/Shared/Formatting/MetricUnitFormatUtils.lua +924 -0
- package/src/Shared/Formatting/MetricUnitFormatUtils.spec.lua +280 -0
- package/src/Shared/Formatting/RxMetricUnitFormatUtils.lua +62 -0
- package/src/Shared/Localization/MetricUnitFallbackStrings.lua +289 -0
- package/src/Shared/Localization/MetricUnitFallbackStrings.spec.lua +82 -0
- package/src/Shared/Localization/MetricUnitTranslator/ar.json.txt +182 -0
- package/src/Shared/Localization/MetricUnitTranslator/de.json.txt +182 -0
- package/src/Shared/Localization/MetricUnitTranslator/en.json.txt +182 -0
- package/src/Shared/Localization/MetricUnitTranslator/es.json.txt +182 -0
- package/src/Shared/Localization/MetricUnitTranslator/fr.json.txt +182 -0
- package/src/Shared/Localization/MetricUnitTranslator/id.json.txt +182 -0
- package/src/Shared/Localization/MetricUnitTranslator/init.lua +92 -0
- package/src/Shared/Localization/MetricUnitTranslator/it.json.txt +182 -0
- package/src/Shared/Localization/MetricUnitTranslator/ja.json.txt +182 -0
- package/src/Shared/Localization/MetricUnitTranslator/ko.json.txt +182 -0
- package/src/Shared/Localization/MetricUnitTranslator/pl.json.txt +182 -0
- package/src/Shared/Localization/MetricUnitTranslator/pt.json.txt +182 -0
- package/src/Shared/Localization/MetricUnitTranslator/ru.json.txt +182 -0
- package/src/Shared/Localization/MetricUnitTranslator/th.json.txt +182 -0
- package/src/Shared/Localization/MetricUnitTranslator/tr.json.txt +182 -0
- package/src/Shared/Localization/MetricUnitTranslator/vi.json.txt +182 -0
- package/src/Shared/Localization/MetricUnitTranslator/zh-cn.json.txt +182 -0
- package/src/Shared/Localization/MetricUnitTranslator/zh-tw.json.txt +182 -0
- package/src/Shared/Localization/MetricUnitTranslator.spec.lua +309 -0
- package/src/Shared/Localization/PluralCategoryUtils.lua +103 -0
- package/src/Shared/Localization/PluralCategoryUtils.spec.lua +77 -0
- package/src/Shared/Logarithmic/DecibelUtils.lua +72 -0
- package/src/Shared/Logarithmic/DecibelUtils.spec.lua +35 -0
- package/src/Shared/MetricUnit.Angles.spec.lua +80 -0
- package/src/Shared/MetricUnit.Customary.spec.lua +104 -0
- package/src/Shared/MetricUnit.Derived.spec.lua +173 -0
- package/src/Shared/MetricUnit.Display.spec.lua +153 -0
- package/src/Shared/MetricUnit.Electrical.spec.lua +114 -0
- package/src/Shared/MetricUnit.Math.spec.lua +112 -0
- package/src/Shared/MetricUnit.Roblox.spec.lua +34 -0
- package/src/Shared/MetricUnit.SI.spec.lua +127 -0
- package/src/Shared/MetricUnit.Spring.spec.lua +94 -0
- package/src/Shared/MetricUnit.Temperature.spec.lua +212 -0
- package/src/Shared/MetricUnit.Time.spec.lua +202 -0
- package/src/Shared/MetricUnit.Units.spec.lua +210 -0
- package/src/Shared/MetricUnit.lua +1730 -0
- package/src/Shared/MetricUnit.spec.lua +362 -0
- package/src/Shared/MetricUtils.lua +47 -16
- package/src/Shared/MetricUtils.spec.lua +76 -0
- package/src/Shared/Parsing/MetricUnitParseUtils.lua +799 -0
- package/src/Shared/Parsing/MetricUnitParseUtils.spec.lua +189 -0
- package/src/Shared/Systems/MetricUnitSystem.lua +322 -0
- package/src/Shared/Systems/MetricUnitSystem.spec.lua +181 -0
- package/src/Shared/Systems/MetricUnitSystems.lua +225 -0
- package/src/jest.config.lua +3 -0
- package/src/node_modules.project.json +7 -0
- package/test/default.project.json +25 -0
- package/test/scripts/Server/ServerMain.server.lua +14 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--[=[
|
|
3
|
+
CLDR cardinal plural rules: which form of a word (`one`, `few`, `many`, `other`, …) a number takes in a language.
|
|
4
|
+
Nothing here is specific to units; [MetricUnitFormatUtils] uses it to pick a translated unit name's form.
|
|
5
|
+
|
|
6
|
+
@class PluralCategoryUtils
|
|
7
|
+
]=]
|
|
8
|
+
|
|
9
|
+
local require = require(script.Parent.loader).load(script)
|
|
10
|
+
|
|
11
|
+
local ResolveLocaleUtils = require("ResolveLocaleUtils")
|
|
12
|
+
|
|
13
|
+
local PluralCategoryUtils = {}
|
|
14
|
+
|
|
15
|
+
local DEFAULT_LOCALE = "en-us"
|
|
16
|
+
|
|
17
|
+
--[=[
|
|
18
|
+
A CLDR plural category. A translated name has a key for each category its language uses.
|
|
19
|
+
|
|
20
|
+
@type PluralCategory "zero" | "one" | "two" | "few" | "many" | "other"
|
|
21
|
+
@within PluralCategoryUtils
|
|
22
|
+
]=]
|
|
23
|
+
export type PluralCategory = "zero" | "one" | "two" | "few" | "many" | "other"
|
|
24
|
+
|
|
25
|
+
--[=[
|
|
26
|
+
The CLDR plural category of a number as written in `locale`, from CLDR's cardinal plural rules for every language
|
|
27
|
+
[MetricUnitTranslator] ships. `amount` is the rounded value shown and `fraction` the decimals written after the
|
|
28
|
+
decimal mark (`"50"` for "1.50"), since English writes "1 meter" but "1.0 meters".
|
|
29
|
+
|
|
30
|
+
- `one` only: English, German (`i = 1 and v = 0`); Spanish and Turkish (`n = 1`); Vietnamese (`i = 0 or n = 1`).
|
|
31
|
+
- `one` and `many`: French and Portuguese (`one` for `i = 0..1`), Italian (`one` for `i = 1 and v = 0`); `many`
|
|
32
|
+
is a whole multiple of a million, as Spanish also has.
|
|
33
|
+
- `one`, `few`, `many`: Russian and Polish, for whole numbers; their fractions are `other`.
|
|
34
|
+
- `zero`, `one`, `two`, `few`, `many`: Arabic.
|
|
35
|
+
- `other` only: Chinese, Japanese, Korean, Thai and Indonesian.
|
|
36
|
+
|
|
37
|
+
```lua
|
|
38
|
+
PluralCategoryUtils.getPluralCategory("en-us", 1) -- "one"
|
|
39
|
+
PluralCategoryUtils.getPluralCategory("en-us", 1, "0") -- "other", as in "1.0 meters"
|
|
40
|
+
PluralCategoryUtils.getPluralCategory("fr-fr", 1.5, "5") -- "one"
|
|
41
|
+
PluralCategoryUtils.getPluralCategory("ru-ru", 3) -- "few"
|
|
42
|
+
PluralCategoryUtils.getPluralCategory("ar", 11) -- "many"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
@param locale string?
|
|
46
|
+
@param amount number
|
|
47
|
+
@param fraction string? -- the decimals written, if any
|
|
48
|
+
@return PluralCategory
|
|
49
|
+
]=]
|
|
50
|
+
function PluralCategoryUtils.getPluralCategory(locale: string?, amount: number, fraction: string?): PluralCategory
|
|
51
|
+
local language = ResolveLocaleUtils.getLanguageSubtag(locale or DEFAULT_LOCALE) or "en"
|
|
52
|
+
|
|
53
|
+
local absoluteValue = math.abs(amount)
|
|
54
|
+
local integerPart = math.floor(absoluteValue)
|
|
55
|
+
local decimalsWritten = if fraction then #fraction elseif absoluteValue ~= integerPart then 1 else 0
|
|
56
|
+
local isWholeMillion = decimalsWritten == 0 and integerPart ~= 0 and integerPart % 1_000_000 == 0
|
|
57
|
+
|
|
58
|
+
if language == "en" or language == "de" then
|
|
59
|
+
return if integerPart == 1 and decimalsWritten == 0 then "one" else "other"
|
|
60
|
+
elseif language == "es" then
|
|
61
|
+
return if absoluteValue == 1 then "one" elseif isWholeMillion then "many" else "other"
|
|
62
|
+
elseif language == "fr" or language == "pt" then
|
|
63
|
+
return if integerPart == 0 or integerPart == 1 then "one" elseif isWholeMillion then "many" else "other"
|
|
64
|
+
elseif language == "it" then
|
|
65
|
+
return if integerPart == 1 and decimalsWritten == 0 then "one" elseif isWholeMillion then "many" else "other"
|
|
66
|
+
elseif language == "tr" then
|
|
67
|
+
return if absoluteValue == 1 then "one" else "other"
|
|
68
|
+
elseif language == "vi" then
|
|
69
|
+
return if integerPart == 0 or absoluteValue == 1 then "one" else "other"
|
|
70
|
+
elseif language == "ru" or language == "pl" then
|
|
71
|
+
if decimalsWritten ~= 0 then
|
|
72
|
+
return "other"
|
|
73
|
+
end
|
|
74
|
+
local ones, tens = integerPart % 10, integerPart % 100
|
|
75
|
+
if language == "pl" and integerPart == 1 then
|
|
76
|
+
return "one"
|
|
77
|
+
elseif language == "ru" and ones == 1 and tens ~= 11 then
|
|
78
|
+
return "one"
|
|
79
|
+
elseif ones >= 2 and ones <= 4 and (tens < 12 or tens > 14) then
|
|
80
|
+
return "few"
|
|
81
|
+
end
|
|
82
|
+
return "many"
|
|
83
|
+
elseif language == "ar" then
|
|
84
|
+
if absoluteValue == 0 then
|
|
85
|
+
return "zero"
|
|
86
|
+
elseif absoluteValue == 1 then
|
|
87
|
+
return "one"
|
|
88
|
+
elseif absoluteValue == 2 then
|
|
89
|
+
return "two"
|
|
90
|
+
elseif absoluteValue == integerPart then
|
|
91
|
+
local lastTwoDigits = integerPart % 100
|
|
92
|
+
if lastTwoDigits >= 3 and lastTwoDigits <= 10 then
|
|
93
|
+
return "few"
|
|
94
|
+
elseif lastTwoDigits >= 11 then
|
|
95
|
+
return "many"
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
return "other"
|
|
99
|
+
end
|
|
100
|
+
return "other"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
return table.freeze(PluralCategoryUtils)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--[[
|
|
3
|
+
@class PluralCategoryUtils.spec.lua
|
|
4
|
+
]]
|
|
5
|
+
|
|
6
|
+
local require = require(script.Parent.loader).load(script)
|
|
7
|
+
|
|
8
|
+
local Jest = require("Jest")
|
|
9
|
+
local PluralCategoryUtils = require("PluralCategoryUtils")
|
|
10
|
+
|
|
11
|
+
local describe = Jest.Globals.describe
|
|
12
|
+
local it = Jest.Globals.it
|
|
13
|
+
|
|
14
|
+
describe("PluralCategoryUtils.getPluralCategory", function()
|
|
15
|
+
-- CLDR's samples for each category, as fraction digits written ("" for a whole number)
|
|
16
|
+
local SAMPLES: { [string]: { { any } } } = {
|
|
17
|
+
en = { { 1, "", "one" }, { 0, "", "other" }, { 2, "", "other" }, { 1, "0", "other" }, { 1.5, "5", "other" } },
|
|
18
|
+
de = { { 1, "", "one" }, { 2, "", "other" }, { 1, "0", "other" } },
|
|
19
|
+
es = { { 1, "", "one" }, { 1, "0", "one" }, { 2, "", "other" }, { 1000000, "", "many" }, { 0.5, "5", "other" } },
|
|
20
|
+
fr = { { 0, "", "one" }, { 1, "", "one" }, { 1.5, "5", "one" }, { 2, "", "other" }, { 1000000, "", "many" } },
|
|
21
|
+
pt = { { 0, "", "one" }, { 1.5, "5", "one" }, { 2, "", "other" }, { 2000000, "", "many" } },
|
|
22
|
+
it = { { 1, "", "one" }, { 1, "0", "other" }, { 2, "", "other" }, { 1000000, "", "many" } },
|
|
23
|
+
ru = {
|
|
24
|
+
{ 1, "", "one" },
|
|
25
|
+
{ 21, "", "one" },
|
|
26
|
+
{ 2, "", "few" },
|
|
27
|
+
{ 24, "", "few" },
|
|
28
|
+
{ 0, "", "many" },
|
|
29
|
+
{ 5, "", "many" },
|
|
30
|
+
{ 11, "", "many" },
|
|
31
|
+
{ 12, "", "many" },
|
|
32
|
+
{ 1.5, "5", "other" },
|
|
33
|
+
},
|
|
34
|
+
pl = {
|
|
35
|
+
{ 1, "", "one" },
|
|
36
|
+
{ 2, "", "few" },
|
|
37
|
+
{ 22, "", "few" },
|
|
38
|
+
{ 0, "", "many" },
|
|
39
|
+
{ 5, "", "many" },
|
|
40
|
+
{ 21, "", "many" },
|
|
41
|
+
{ 12, "", "many" },
|
|
42
|
+
{ 1.5, "5", "other" },
|
|
43
|
+
},
|
|
44
|
+
tr = { { 1, "", "one" }, { 2, "", "other" }, { 1, "0", "one" } },
|
|
45
|
+
vi = { { 0, "", "one" }, { 1, "", "one" }, { 0.5, "5", "one" }, { 2, "", "other" } },
|
|
46
|
+
ar = {
|
|
47
|
+
{ 0, "", "zero" },
|
|
48
|
+
{ 1, "", "one" },
|
|
49
|
+
{ 2, "", "two" },
|
|
50
|
+
{ 3, "", "few" },
|
|
51
|
+
{ 103, "", "few" },
|
|
52
|
+
{ 11, "", "many" },
|
|
53
|
+
{ 99, "", "many" },
|
|
54
|
+
{ 100, "", "other" },
|
|
55
|
+
{ 102, "", "other" },
|
|
56
|
+
{ 1.5, "5", "other" },
|
|
57
|
+
},
|
|
58
|
+
id = { { 1, "", "other" } },
|
|
59
|
+
th = { { 1, "", "other" } },
|
|
60
|
+
ja = { { 1, "", "other" } },
|
|
61
|
+
ko = { { 1, "", "other" } },
|
|
62
|
+
["zh-cn"] = { { 1, "", "other" } },
|
|
63
|
+
["zh-tw"] = { { 1, "", "other" } },
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
it("follows CLDR's rule for every shipped language", function()
|
|
67
|
+
for locale, samples in SAMPLES do
|
|
68
|
+
for _, sample in samples do
|
|
69
|
+
local fraction = if sample[2] == "" then nil else sample[2]
|
|
70
|
+
local category = PluralCategoryUtils.getPluralCategory(locale, sample[1], fraction)
|
|
71
|
+
if category ~= sample[3] then
|
|
72
|
+
error(string.format("%s %s: expected %s, got %s", locale, tostring(sample[1]), sample[3], category))
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end)
|
|
77
|
+
end)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--[=[
|
|
3
|
+
Converts between linear ratios and decibels. A decibel value is a logarithm, so it is a plain number here rather
|
|
4
|
+
than a [MetricUnit]: decibel gains add where linear ratios multiply, which does not fit MetricUnit's arithmetic.
|
|
5
|
+
|
|
6
|
+
Amplitude ratios (a signal's level, such as a voltage or a sample value) use 20 × log10; power ratios use
|
|
7
|
+
10 × log10. Halving the amplitude is about -6 dB; halving the power is about -3 dB.
|
|
8
|
+
|
|
9
|
+
```lua
|
|
10
|
+
DecibelUtils.amplitudeToDecibels(0.5) -- -6.0206
|
|
11
|
+
DecibelUtils.decibelsToAmplitude(-6) -- 0.5012
|
|
12
|
+
DecibelUtils.powerToDecibels(2) -- 3.0103
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Check which kind a property is before converting: a volume that scales a signal is an amplitude ratio.
|
|
16
|
+
|
|
17
|
+
@class DecibelUtils
|
|
18
|
+
]=]
|
|
19
|
+
|
|
20
|
+
local DecibelUtils = {}
|
|
21
|
+
|
|
22
|
+
--[=[
|
|
23
|
+
The decibels of an amplitude ratio: `20 × log10(ratio)`. A ratio of 0 is negative infinity (silence).
|
|
24
|
+
|
|
25
|
+
@param ratio number -- 1 is unchanged
|
|
26
|
+
@return number
|
|
27
|
+
]=]
|
|
28
|
+
function DecibelUtils.amplitudeToDecibels(ratio: number): number
|
|
29
|
+
assert(type(ratio) == "number" and ratio >= 0, "Bad ratio")
|
|
30
|
+
if ratio == 0 then
|
|
31
|
+
return -math.huge
|
|
32
|
+
end
|
|
33
|
+
return 20 * math.log10(ratio)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
--[=[
|
|
37
|
+
The amplitude ratio of a decibel gain: `10 ^ (decibels / 20)`.
|
|
38
|
+
|
|
39
|
+
@param decibels number -- 0 is unchanged
|
|
40
|
+
@return number
|
|
41
|
+
]=]
|
|
42
|
+
function DecibelUtils.decibelsToAmplitude(decibels: number): number
|
|
43
|
+
assert(type(decibels) == "number", "Bad decibels")
|
|
44
|
+
return 10 ^ (decibels / 20)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
--[=[
|
|
48
|
+
The decibels of a power ratio: `10 × log10(ratio)`. A ratio of 0 is negative infinity.
|
|
49
|
+
|
|
50
|
+
@param ratio number -- 1 is unchanged
|
|
51
|
+
@return number
|
|
52
|
+
]=]
|
|
53
|
+
function DecibelUtils.powerToDecibels(ratio: number): number
|
|
54
|
+
assert(type(ratio) == "number" and ratio >= 0, "Bad ratio")
|
|
55
|
+
if ratio == 0 then
|
|
56
|
+
return -math.huge
|
|
57
|
+
end
|
|
58
|
+
return 10 * math.log10(ratio)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
--[=[
|
|
62
|
+
The power ratio of a decibel gain: `10 ^ (decibels / 10)`.
|
|
63
|
+
|
|
64
|
+
@param decibels number -- 0 is unchanged
|
|
65
|
+
@return number
|
|
66
|
+
]=]
|
|
67
|
+
function DecibelUtils.decibelsToPower(decibels: number): number
|
|
68
|
+
assert(type(decibels) == "number", "Bad decibels")
|
|
69
|
+
return 10 ^ (decibels / 10)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
return table.freeze(DecibelUtils)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--[[
|
|
3
|
+
@class DecibelUtils.spec.lua
|
|
4
|
+
]]
|
|
5
|
+
|
|
6
|
+
local require = require(script.Parent.loader).load(script)
|
|
7
|
+
|
|
8
|
+
local DecibelUtils = require("DecibelUtils")
|
|
9
|
+
local Jest = require("Jest")
|
|
10
|
+
|
|
11
|
+
local describe = Jest.Globals.describe
|
|
12
|
+
local expect = Jest.Globals.expect
|
|
13
|
+
local it = Jest.Globals.it
|
|
14
|
+
|
|
15
|
+
describe("DecibelUtils", function()
|
|
16
|
+
it("uses 20 log10 for amplitude ratios", function()
|
|
17
|
+
expect(DecibelUtils.amplitudeToDecibels(1)).toBe(0)
|
|
18
|
+
expect(DecibelUtils.amplitudeToDecibels(10)).toBeCloseTo(20, 9)
|
|
19
|
+
expect(DecibelUtils.amplitudeToDecibels(0.5)).toBeCloseTo(-6.0206, 4)
|
|
20
|
+
expect(DecibelUtils.decibelsToAmplitude(-20)).toBeCloseTo(0.1, 12)
|
|
21
|
+
expect(DecibelUtils.amplitudeToDecibels(0)).toBe(-math.huge)
|
|
22
|
+
end)
|
|
23
|
+
|
|
24
|
+
it("uses 10 log10 for power ratios", function()
|
|
25
|
+
expect(DecibelUtils.powerToDecibels(10)).toBeCloseTo(10, 9)
|
|
26
|
+
expect(DecibelUtils.powerToDecibels(2)).toBeCloseTo(3.0103, 4)
|
|
27
|
+
expect(DecibelUtils.decibelsToPower(-10)).toBeCloseTo(0.1, 12)
|
|
28
|
+
end)
|
|
29
|
+
|
|
30
|
+
it("round trips, and adds gains where ratios multiply", function()
|
|
31
|
+
expect(DecibelUtils.decibelsToAmplitude(DecibelUtils.amplitudeToDecibels(0.37))).toBeCloseTo(0.37, 12)
|
|
32
|
+
local gain = DecibelUtils.amplitudeToDecibels(0.5) + DecibelUtils.amplitudeToDecibels(0.5)
|
|
33
|
+
expect(DecibelUtils.decibelsToAmplitude(gain)).toBeCloseTo(0.25, 12)
|
|
34
|
+
end)
|
|
35
|
+
end)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--[[
|
|
3
|
+
@class MetricUnit.Angles.spec.lua
|
|
4
|
+
]]
|
|
5
|
+
|
|
6
|
+
local require = require(script.Parent.loader).load(script)
|
|
7
|
+
|
|
8
|
+
local Jest = require("Jest")
|
|
9
|
+
local MetricUnit = require("MetricUnit")
|
|
10
|
+
|
|
11
|
+
local describe = Jest.Globals.describe
|
|
12
|
+
local expect = Jest.Globals.expect
|
|
13
|
+
local it = Jest.Globals.it
|
|
14
|
+
|
|
15
|
+
-- Formatting puts a no-break space between a number and its unit; these compare it as a plain space
|
|
16
|
+
local function plain(text: string): string
|
|
17
|
+
return (string.gsub(text, "\u{00A0}", " "))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
local function format(value: any, options: MetricUnit.FormatOptions?): string
|
|
21
|
+
return plain(MetricUnit.format(value, options))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
describe("MetricUnit angles", function()
|
|
25
|
+
it("converts between radians, degrees and revolutions", function()
|
|
26
|
+
expect(MetricUnit.degrees(180):In("radian")).toBeCloseTo(math.pi, 12)
|
|
27
|
+
expect(MetricUnit.new(1, "revolution"):In("degree")).toBeCloseTo(360, 9)
|
|
28
|
+
expect(MetricUnit.fuzzyEq(MetricUnit.radians(math.pi), MetricUnit.degrees(180))).toBe(true)
|
|
29
|
+
-- The bridge to CFrame.Angles and math.sin is reading radians
|
|
30
|
+
expect(MetricUnit.degrees(90):In("radian")).toBeCloseTo(math.rad(90), 12)
|
|
31
|
+
expect(MetricUnit.radians(math.rad(30)):In("degree")).toBeCloseTo(30, 9)
|
|
32
|
+
expect(MetricUnit.degrees(1):GetKind()).toBe("angle")
|
|
33
|
+
end)
|
|
34
|
+
|
|
35
|
+
it("keeps angles apart from lengths", function()
|
|
36
|
+
expect(function()
|
|
37
|
+
local _ = MetricUnit.degrees(1) + MetricUnit.meters(1)
|
|
38
|
+
end).toThrow("cannot add angle and length")
|
|
39
|
+
expect(MetricUnit.sameDimension(MetricUnit.radians(1), MetricUnit.none(1))).toBe(false)
|
|
40
|
+
end)
|
|
41
|
+
|
|
42
|
+
it("reads angular velocity and acceleration", function()
|
|
43
|
+
expect(MetricUnit.new(60, "revolutionPerMinute"):In("radianPerSecond")).toBeCloseTo(2 * math.pi, 9)
|
|
44
|
+
expect(MetricUnit.new(360, "degreePerSecond"):In("revolutionPerMinute")).toBeCloseTo(60, 9)
|
|
45
|
+
expect((MetricUnit.degrees(90) / MetricUnit.seconds(1)):GetKind()).toBe("angularVelocity")
|
|
46
|
+
local spinUp = MetricUnit.new(4, "radianPerSecond") / MetricUnit.seconds(2)
|
|
47
|
+
expect(spinUp:GetKind()).toBe("angularAcceleration")
|
|
48
|
+
expect(spinUp:In("radianPerSecondSquared")).toBe(2)
|
|
49
|
+
end)
|
|
50
|
+
|
|
51
|
+
it("turns an angle into an arc length, and an angular speed into a speed", function()
|
|
52
|
+
expect(MetricUnit.arcLength(MetricUnit.degrees(180), MetricUnit.meters(2)):In("meter")).toBeCloseTo(
|
|
53
|
+
2 * math.pi,
|
|
54
|
+
9
|
|
55
|
+
)
|
|
56
|
+
local rim = MetricUnit.arcLength(MetricUnit.new(60, "revolutionPerMinute"), MetricUnit.meters(1))
|
|
57
|
+
expect(rim:GetKind()).toBe("speed")
|
|
58
|
+
expect(rim:In("meterPerSecond")).toBeCloseTo(2 * math.pi, 9)
|
|
59
|
+
expect(function()
|
|
60
|
+
MetricUnit.arcLength(MetricUnit.meters(1), MetricUnit.meters(1))
|
|
61
|
+
end).toThrow("takes an angle or an angle per time, not length")
|
|
62
|
+
end)
|
|
63
|
+
|
|
64
|
+
it("formats angles in degrees by default", function()
|
|
65
|
+
expect(format(MetricUnit.degrees(45))).toBe("45°")
|
|
66
|
+
expect(format(MetricUnit.radians(1))).toBe("57.296°")
|
|
67
|
+
expect(format(MetricUnit.radians(1), { unit = "radian" })).toBe("1 rad")
|
|
68
|
+
expect(format(MetricUnit.degrees(2), { unitDisplay = "long" })).toBe("2 degrees")
|
|
69
|
+
expect(format(MetricUnit.degrees(1), { unitDisplay = "long" })).toBe("1 degree")
|
|
70
|
+
expect(format(MetricUnit.new(60, "revolutionPerMinute"))).toBe("360 °/s")
|
|
71
|
+
expect(format(MetricUnit.new(60, "revolutionPerMinute"), { unit = "revolutionPerMinute" })).toBe("60 rpm")
|
|
72
|
+
expect(tostring(MetricUnit.degrees(45))).toBe("45°")
|
|
73
|
+
end)
|
|
74
|
+
end)
|
|
75
|
+
|
|
76
|
+
describe("MetricUnit.arcLength with a plain zero radius", function()
|
|
77
|
+
it("is zero", function()
|
|
78
|
+
expect(MetricUnit.arcLength(MetricUnit.degrees(90), 0 :: any):In("meter")).toBe(0)
|
|
79
|
+
end)
|
|
80
|
+
end)
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--[[
|
|
3
|
+
@class MetricUnit.Customary.spec.lua
|
|
4
|
+
]]
|
|
5
|
+
|
|
6
|
+
local require = require(script.Parent.loader).load(script)
|
|
7
|
+
|
|
8
|
+
local Jest = require("Jest")
|
|
9
|
+
local MetricUnit = require("MetricUnit")
|
|
10
|
+
local MetricUnitSystems = require("MetricUnitSystems")
|
|
11
|
+
|
|
12
|
+
local describe = Jest.Globals.describe
|
|
13
|
+
local expect = Jest.Globals.expect
|
|
14
|
+
local it = Jest.Globals.it
|
|
15
|
+
|
|
16
|
+
local function format(value: any, options: any?): string
|
|
17
|
+
return (string.gsub(MetricUnit.format(value, options), "\u{00A0}", " "))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe("MetricUnit US customary and British imperial", function()
|
|
21
|
+
it("keeps US and imperial volumes apart", function()
|
|
22
|
+
expect(MetricUnit.new(1, "usGallon"):In("liter")).toBeCloseTo(3.785411784, 9)
|
|
23
|
+
expect(MetricUnit.new(1, "imperialGallon"):In("liter")).toBeCloseTo(4.54609, 9)
|
|
24
|
+
expect(MetricUnit.new(1, "imperialPint"):In("usPint")).toBeCloseTo(1.20095, 5)
|
|
25
|
+
expect(MetricUnit.new(1, "usCup"):In("usTablespoon")).toBeCloseTo(16, 9)
|
|
26
|
+
expect(MetricUnit.new(1, "usTablespoon"):In("usTeaspoon")).toBeCloseTo(3, 9)
|
|
27
|
+
expect(MetricUnit.new(1, "imperialPint"):In("imperialFluidOunce")).toBeCloseTo(20, 9)
|
|
28
|
+
end)
|
|
29
|
+
|
|
30
|
+
it("shows each system's units for a value that asks for it", function()
|
|
31
|
+
local volume = MetricUnit.new(2, "liter")
|
|
32
|
+
expect(format(volume:ToUSCustomary())).toBe("2.113 US qt")
|
|
33
|
+
expect(format(volume:ToImperial())).toBe("3.52 imp pt")
|
|
34
|
+
expect(format(MetricUnit.new(250, "milliliter"):ToUSCustomary())).toBe("1.057 cups")
|
|
35
|
+
expect(format(MetricUnit.new(5, "milliliter"):ToUSCustomary())).toBe("1.014 tsp")
|
|
36
|
+
|
|
37
|
+
local body = MetricUnit.kilograms(80)
|
|
38
|
+
expect(format(body:ToUSCustomary())).toBe("176.37 lb")
|
|
39
|
+
expect(format(body:ToImperial())).toBe("12.598 st")
|
|
40
|
+
expect(format(MetricUnit.new(2, "tonne"):ToUSCustomary())).toBe("2.205 tn")
|
|
41
|
+
|
|
42
|
+
local distance = MetricUnit.kilometers(5)
|
|
43
|
+
expect(format(distance:ToUSCustomary())).toBe("3.107 mi")
|
|
44
|
+
expect(format(distance:ToImperial())).toBe("3.107 mi")
|
|
45
|
+
expect(distance:ToImperial():GetDisplaySystem()).toBe(MetricUnitSystems.IMPERIAL)
|
|
46
|
+
end)
|
|
47
|
+
|
|
48
|
+
it("covers areas, volumes and densities in feet and inches", function()
|
|
49
|
+
expect(MetricUnit.new(1, "squareMile"):In("acre")).toBeCloseTo(640, 9)
|
|
50
|
+
expect(MetricUnit.new(1, "cubicFoot"):In("cubicInch")).toBeCloseTo(1728, 9)
|
|
51
|
+
expect(MetricUnit.new(1, "cubicYard"):In("cubicFoot")).toBeCloseTo(27, 9)
|
|
52
|
+
expect(MetricUnit.new(1, "usGallon"):In("cubicInch")).toBeCloseTo(231, 9)
|
|
53
|
+
expect(MetricUnit.new(62.428, "poundPerCubicFoot"):In("kilogramPerCubicMeter")).toBeCloseTo(1000, 2)
|
|
54
|
+
expect(MetricUnit.new(1, "nauticalMile"):In("meter")).toBe(1852)
|
|
55
|
+
end)
|
|
56
|
+
end)
|
|
57
|
+
|
|
58
|
+
describe("MetricUnit for cars", function()
|
|
59
|
+
it("converts horsepower, torque and fuel economy", function()
|
|
60
|
+
expect(MetricUnit.new(1, "horsepower"):In("watt")).toBeCloseTo(745.6999, 4)
|
|
61
|
+
expect(MetricUnit.new(1, "metricHorsepower"):In("watt")).toBeCloseTo(735.49875, 9)
|
|
62
|
+
expect(format(MetricUnit.new(100, "kilowatt"):ToUSCustomary())).toBe("134.102 hp")
|
|
63
|
+
|
|
64
|
+
local torque = MetricUnit.new(300, "poundFoot")
|
|
65
|
+
expect(torque:In("newtonMeter")).toBeCloseTo(406.7454, 4)
|
|
66
|
+
expect(format(torque:As("newtonMeter"))).toBe("406.745 N·m")
|
|
67
|
+
|
|
68
|
+
local economy = MetricUnit.new(30, "milePerUsGallon")
|
|
69
|
+
expect(economy:GetKind()).toBe("fuelEconomy")
|
|
70
|
+
expect(economy:In("kilometerPerLiter")).toBeCloseTo(12.7543, 4)
|
|
71
|
+
expect(economy:In("milePerImperialGallon")).toBeCloseTo(36.0285, 4)
|
|
72
|
+
expect((1 / economy):In("literPer100Kilometers")).toBeCloseTo(7.8405, 4)
|
|
73
|
+
expect(format(economy:ToImperial())).toBe("36.028 mpg (UK)")
|
|
74
|
+
expect(format(economy)).toBe("30 mpg (US)")
|
|
75
|
+
expect(format(economy:ToMetric())).toBe("12.754 km/L")
|
|
76
|
+
end)
|
|
77
|
+
|
|
78
|
+
it("derives fuel economy from a distance and the fuel it used", function()
|
|
79
|
+
local economy = MetricUnit.new(300, "mile") / MetricUnit.new(10, "usGallon")
|
|
80
|
+
expect(economy:In("milePerUsGallon")).toBeCloseTo(30, 9)
|
|
81
|
+
expect(format(economy:ToUSCustomary())).toBe("30 mpg (US)")
|
|
82
|
+
end)
|
|
83
|
+
|
|
84
|
+
it("converts BTU, psi, inches of mercury and Rankine", function()
|
|
85
|
+
expect(MetricUnit.new(1, "britishThermalUnit"):In("joule")).toBeCloseTo(1055.05585262, 9)
|
|
86
|
+
expect(MetricUnit.new(29.92, "inchOfMercury"):In("kilopascal")).toBeCloseTo(101.32, 2)
|
|
87
|
+
expect(MetricUnit.new(32, "poundForcePerSquareInch"):In("kilopascal")).toBeCloseTo(220.632, 3)
|
|
88
|
+
expect(MetricUnit.point(491.67, "rankine"):In("celsius")).toBeCloseTo(0, 9)
|
|
89
|
+
expect(MetricUnit.new(10, "footPerSecondSquared"):In("meterPerSecondSquared")).toBeCloseTo(3.048, 9)
|
|
90
|
+
end)
|
|
91
|
+
|
|
92
|
+
it("parses car units as they are written", function()
|
|
93
|
+
local function parse(text: string): MetricUnit.MetricUnit
|
|
94
|
+
return assert(MetricUnit.parse(text)) :: any
|
|
95
|
+
end
|
|
96
|
+
expect(parse("300 hp"):In("horsepower")).toBe(300)
|
|
97
|
+
expect(parse("400 Nm"):In("newtonMeter")).toBe(400)
|
|
98
|
+
expect(parse("300 lb-ft"):In("poundFoot")).toBe(300)
|
|
99
|
+
expect(parse("30 mpg"):In("milePerUsGallon")).toBe(30)
|
|
100
|
+
expect(parse("12000 BTU"):In("britishThermalUnit")).toBe(12000)
|
|
101
|
+
expect(parse("2 cups"):In("usCup")).toBe(2)
|
|
102
|
+
expect(parse("150 PS"):In("metricHorsepower")).toBe(150)
|
|
103
|
+
end)
|
|
104
|
+
end)
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--[[
|
|
3
|
+
@class MetricUnit.Derived.spec.lua
|
|
4
|
+
]]
|
|
5
|
+
|
|
6
|
+
local require = require(script.Parent.loader).load(script)
|
|
7
|
+
|
|
8
|
+
local Jest = require("Jest")
|
|
9
|
+
local MetricUnit = require("MetricUnit")
|
|
10
|
+
|
|
11
|
+
local describe = Jest.Globals.describe
|
|
12
|
+
local expect = Jest.Globals.expect
|
|
13
|
+
local it = Jest.Globals.it
|
|
14
|
+
|
|
15
|
+
describe("MetricUnit prefixed base units", function()
|
|
16
|
+
it("reads milliseconds and microseconds back", function()
|
|
17
|
+
expect(MetricUnit.milliseconds(250):In("millisecond")).toBeCloseTo(250, 9)
|
|
18
|
+
expect(MetricUnit.new(40, "microsecond"):In("microsecond")).toBeCloseTo(40, 9)
|
|
19
|
+
expect(MetricUnit.milliseconds(1_500):InSeconds()).toBeCloseTo(1.5, 9)
|
|
20
|
+
expect(MetricUnit.new(1_000, "microsecond"):In("millisecond")).toBeCloseTo(1, 9)
|
|
21
|
+
end)
|
|
22
|
+
|
|
23
|
+
it("keeps decimal and binary memory apart", function()
|
|
24
|
+
expect(MetricUnit.new(1, "gigabyte"):In("megabyte")).toBe(1_000)
|
|
25
|
+
expect(MetricUnit.new(1, "kibibyte"):In("byte")).toBe(1_024)
|
|
26
|
+
expect(MetricUnit.new(4, "mebibyte"):In("byte")).toBe(4_194_304)
|
|
27
|
+
expect(MetricUnit.new(1, "gibibyte"):In("mebibyte")).toBe(1_024)
|
|
28
|
+
expect(MetricUnit.new(1, "mebibyte"):In("kibibyte")).toBe(1_024)
|
|
29
|
+
expect(MetricUnit.new(1, "gigabyte"):In("gibibyte")).toBeCloseTo(0.9313, 4)
|
|
30
|
+
expect(MetricUnit.new(1, "kibibyte"):In("kilobyte")).toBeCloseTo(1.024, 9)
|
|
31
|
+
end)
|
|
32
|
+
|
|
33
|
+
it("reads milligrams back", function()
|
|
34
|
+
expect(MetricUnit.new(500, "milligram"):In("gram")).toBeCloseTo(0.5, 9)
|
|
35
|
+
expect(MetricUnit.grams(2):In("milligram")).toBeCloseTo(2_000, 9)
|
|
36
|
+
end)
|
|
37
|
+
|
|
38
|
+
it("reads a rate per minute and a frequency in hertz", function()
|
|
39
|
+
expect(MetricUnit.new(120, "perMinute"):In("perSecond")).toBeCloseTo(2, 9)
|
|
40
|
+
expect(MetricUnit.perSecond(1):In("perMinute")).toBeCloseTo(60, 9)
|
|
41
|
+
expect(MetricUnit.new(60, "hertz"):In("hertz")).toBe(60)
|
|
42
|
+
expect(MetricUnit.new(1, "hertz") == MetricUnit.perSecond(1)).toBe(true)
|
|
43
|
+
expect((1 / MetricUnit.milliseconds(20)):In("hertz")).toBeCloseTo(50, 9)
|
|
44
|
+
end)
|
|
45
|
+
end)
|
|
46
|
+
|
|
47
|
+
describe("MetricUnit area and volume", function()
|
|
48
|
+
it("reads areas back, from lengths multiplied", function()
|
|
49
|
+
expect(MetricUnit.new(50, "squareMeter"):In("squareMeter")).toBe(50)
|
|
50
|
+
expect(MetricUnit.new(1, "hectare"):In("squareMeter")).toBe(10_000)
|
|
51
|
+
expect(MetricUnit.squareKilometers(1):In("hectare")).toBe(100)
|
|
52
|
+
expect((MetricUnit.meters(100) * MetricUnit.meters(100)):In("hectare")).toBeCloseTo(1, 9)
|
|
53
|
+
expect(MetricUnit.isKind(MetricUnit.kilometers(2) * MetricUnit.kilometers(3), "area")).toBe(true)
|
|
54
|
+
expect((MetricUnit.kilometers(2) * MetricUnit.kilometers(3)):In("squareKilometer")).toBeCloseTo(6, 9)
|
|
55
|
+
end)
|
|
56
|
+
|
|
57
|
+
it("reads volumes back, from areas and lengths multiplied", function()
|
|
58
|
+
expect(MetricUnit.new(1, "liter"):In("milliliter")).toBeCloseTo(1_000, 9)
|
|
59
|
+
expect(MetricUnit.new(1, "cubicMeter"):In("liter")).toBeCloseTo(1_000, 9)
|
|
60
|
+
expect(MetricUnit.new(250, "milliliter"):In("liter")).toBeCloseTo(0.25, 9)
|
|
61
|
+
expect(MetricUnit.isKind(MetricUnit.pow(MetricUnit.new(10, "centimeter"), 3), "volume")).toBe(true)
|
|
62
|
+
expect(MetricUnit.pow(MetricUnit.new(10, "centimeter"), 3):In("liter")).toBeCloseTo(1, 9)
|
|
63
|
+
expect(MetricUnit.isKind(MetricUnit.new(2, "squareMeter") * MetricUnit.meters(3), "volume")).toBe(true)
|
|
64
|
+
end)
|
|
65
|
+
end)
|
|
66
|
+
|
|
67
|
+
describe("MetricUnit mechanics", function()
|
|
68
|
+
it("reads speeds in m/s and km/h", function()
|
|
69
|
+
expect(MetricUnit.new(10, "meterPerSecond"):In("kilometerPerHour")).toBeCloseTo(36, 9)
|
|
70
|
+
expect(MetricUnit.new(36, "kilometerPerHour"):In("meterPerSecond")).toBeCloseTo(10, 9)
|
|
71
|
+
expect((MetricUnit.kilometers(36) / MetricUnit.hours(1)):In("meterPerSecond")).toBeCloseTo(10, 9)
|
|
72
|
+
expect(MetricUnit.isKind(MetricUnit.new(1, "kilometerPerHour"), "speed")).toBe(true)
|
|
73
|
+
end)
|
|
74
|
+
|
|
75
|
+
it("makes an acceleration from a speed over a time", function()
|
|
76
|
+
local acceleration = MetricUnit.new(20, "meterPerSecond") / MetricUnit.seconds(2)
|
|
77
|
+
expect(MetricUnit.isKind(acceleration, "acceleration")).toBe(true)
|
|
78
|
+
expect(acceleration:In("meterPerSecondSquared")).toBeCloseTo(10, 9)
|
|
79
|
+
expect(MetricUnit.new(9.81, "meterPerSecondSquared"):In("meterPerSecondSquared")).toBe(9.81)
|
|
80
|
+
end)
|
|
81
|
+
|
|
82
|
+
it("makes a newton from a mass times an acceleration", function()
|
|
83
|
+
local force = MetricUnit.kilograms(2) * MetricUnit.new(3, "meterPerSecondSquared")
|
|
84
|
+
expect(MetricUnit.isKind(force, "force")).toBe(true)
|
|
85
|
+
expect(force:In("newton")).toBeCloseTo(6, 9)
|
|
86
|
+
expect(force == MetricUnit.new(6, "newton")).toBe(true)
|
|
87
|
+
end)
|
|
88
|
+
|
|
89
|
+
it("makes energy from a force over a length, and power from energy over time", function()
|
|
90
|
+
local work = MetricUnit.new(10, "newton") * MetricUnit.meters(5)
|
|
91
|
+
expect(MetricUnit.isKind(work, "energy")).toBe(true)
|
|
92
|
+
expect(work:In("joule")).toBeCloseTo(50, 9)
|
|
93
|
+
|
|
94
|
+
local power = work / MetricUnit.seconds(2)
|
|
95
|
+
expect(MetricUnit.isKind(power, "power")).toBe(true)
|
|
96
|
+
expect(power:In("watt")).toBeCloseTo(25, 9)
|
|
97
|
+
|
|
98
|
+
expect((MetricUnit.new(2, "kilowatt") * MetricUnit.hours(3)):In("kilowattHour")).toBeCloseTo(6, 9)
|
|
99
|
+
expect((MetricUnit.new(3, "kilowattHour") / MetricUnit.hours(1)):In("kilowatt")).toBeCloseTo(3, 9)
|
|
100
|
+
end)
|
|
101
|
+
|
|
102
|
+
it("converts between energy units", function()
|
|
103
|
+
expect(MetricUnit.new(1, "kilojoule"):In("joule")).toBe(1_000)
|
|
104
|
+
expect(MetricUnit.new(1, "kilowattHour"):In("kilojoule")).toBe(3_600)
|
|
105
|
+
expect(MetricUnit.new(1, "kilocalorie"):In("joule")).toBe(4_184)
|
|
106
|
+
expect(MetricUnit.new(4_184, "joule"):In("kilocalorie")).toBe(1)
|
|
107
|
+
end)
|
|
108
|
+
|
|
109
|
+
it("makes pressure from a force over an area", function()
|
|
110
|
+
local pressure = MetricUnit.new(1_000, "newton") / MetricUnit.new(2, "squareMeter")
|
|
111
|
+
expect(MetricUnit.isKind(pressure, "pressure")).toBe(true)
|
|
112
|
+
expect(pressure:In("pascal")).toBeCloseTo(500, 9)
|
|
113
|
+
expect(MetricUnit.new(1, "bar"):In("kilopascal")).toBe(100)
|
|
114
|
+
expect(MetricUnit.new(250, "kilopascal"):In("bar")).toBeCloseTo(2.5, 9)
|
|
115
|
+
end)
|
|
116
|
+
|
|
117
|
+
it("makes density from a mass over a volume", function()
|
|
118
|
+
local density = MetricUnit.kilograms(1) / MetricUnit.new(1, "liter")
|
|
119
|
+
expect(MetricUnit.isKind(density, "density")).toBe(true)
|
|
120
|
+
expect(density:In("kilogramPerCubicMeter")).toBeCloseTo(1_000, 9)
|
|
121
|
+
expect(density:In("gramPerCubicCentimeter")).toBeCloseTo(1, 9)
|
|
122
|
+
expect(MetricUnit.new(7.8, "gramPerCubicCentimeter"):In("kilogramPerCubicMeter")).toBeCloseTo(7_800, 9)
|
|
123
|
+
expect((MetricUnit.new(1_000, "kilogramPerCubicMeter") * MetricUnit.new(2, "liter")):In("kilogram")).toBeCloseTo(
|
|
124
|
+
2,
|
|
125
|
+
9
|
|
126
|
+
)
|
|
127
|
+
end)
|
|
128
|
+
end)
|
|
129
|
+
|
|
130
|
+
describe("MetricUnit data rates", function()
|
|
131
|
+
it("makes a data rate from memory over time", function()
|
|
132
|
+
local rate = MetricUnit.kilobytes(50) / MetricUnit.seconds(1)
|
|
133
|
+
expect(MetricUnit.isKind(rate, "dataRate")).toBe(true)
|
|
134
|
+
expect(rate:In("kilobytePerSecond")).toBeCloseTo(50, 9)
|
|
135
|
+
expect(MetricUnit.new(2_000, "bytePerSecond"):In("kilobytePerSecond")).toBe(2)
|
|
136
|
+
expect((MetricUnit.new(8, "kilobytePerSecond") * MetricUnit.minutes(1)):In("kilobyte")).toBeCloseTo(480, 9)
|
|
137
|
+
end)
|
|
138
|
+
end)
|
|
139
|
+
|
|
140
|
+
describe("MetricUnit derived boundaries", function()
|
|
141
|
+
it("tells derived dimensions apart", function()
|
|
142
|
+
expect(MetricUnit.isKind(MetricUnit.new(1, "newton"), "energy")).toBe(false)
|
|
143
|
+
expect(MetricUnit.isKind(MetricUnit.new(1, "joule"), "force")).toBe(false)
|
|
144
|
+
expect(MetricUnit.isKind(MetricUnit.new(1, "joule"), "power")).toBe(false)
|
|
145
|
+
expect(MetricUnit.isKind(MetricUnit.new(1, "liter"), "area")).toBe(false)
|
|
146
|
+
expect(MetricUnit.isKind(MetricUnit.perSecond(1), "dataRate")).toBe(false)
|
|
147
|
+
expect(MetricUnit.isKind(MetricUnit.new(1, "meterPerSecondSquared"), "speed")).toBe(false)
|
|
148
|
+
end)
|
|
149
|
+
|
|
150
|
+
it("errors when a derived reader meets another dimension, naming both", function()
|
|
151
|
+
expect(function()
|
|
152
|
+
MetricUnit.new(1, "joule"):In("watt")
|
|
153
|
+
end).toThrow('In("watt") reads length^2·mass/time^3, not length^2·mass/time^2')
|
|
154
|
+
expect(function()
|
|
155
|
+
MetricUnit.new(1, "newton"):In("pascal")
|
|
156
|
+
end).toThrow('In("pascal") reads mass/length/time^2, not length·mass/time^2')
|
|
157
|
+
end)
|
|
158
|
+
|
|
159
|
+
it("expects each derived dimension by name", function()
|
|
160
|
+
expect(MetricUnit.expect(MetricUnit.new(1, "hectare"), "area", "plot")).toBe(10_000)
|
|
161
|
+
expect(MetricUnit.expect(MetricUnit.new(1, "liter"), "volume", "tank")).toBeCloseTo(1e-3, 12)
|
|
162
|
+
expect(MetricUnit.expect(MetricUnit.new(9.81, "meterPerSecondSquared"), "acceleration", "gravity")).toBe(9.81)
|
|
163
|
+
expect(MetricUnit.expect(MetricUnit.new(5, "newton"), "force", "thrust")).toBe(5)
|
|
164
|
+
expect(MetricUnit.expect(MetricUnit.new(2, "kilojoule"), "energy", "charge")).toBe(2_000)
|
|
165
|
+
expect(MetricUnit.expect(MetricUnit.new(1, "kilowatt"), "power", "output")).toBe(1_000)
|
|
166
|
+
expect(MetricUnit.expect(MetricUnit.new(2, "bar"), "pressure", "tyre")).toBe(200_000)
|
|
167
|
+
expect(MetricUnit.expect(MetricUnit.new(997, "kilogramPerCubicMeter"), "density", "water")).toBe(997)
|
|
168
|
+
expect(MetricUnit.expect(MetricUnit.new(1, "kilobytePerSecond"), "dataRate", "budget")).toBe(1_000)
|
|
169
|
+
expect(function()
|
|
170
|
+
MetricUnit.expect(MetricUnit.new(1, "joule"), "power", "output")
|
|
171
|
+
end).toThrow("MetricUnit.expect: output expects length^2·mass/time^3, got length^2·mass/time^2")
|
|
172
|
+
end)
|
|
173
|
+
end)
|