LibWeb: Simplify standalone CSS math functions when used outside calc()
Some checks are pending
CI / Lagom (arm64, Sanitizer_CI, false, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (x86_64, Fuzzers_CI, false, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (x86_64, Sanitizer_CI, false, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (x86_64, Sanitizer_CI, true, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (arm64, macos-15, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (x86_64, ubuntu-24.04, Linux, Linux-x86_64) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run

Math functions like abs(), clamp(), round(), etc, can be used by
themselves in property values, without wrapping them in calc().

Before this change, we were neglecting to run calc simplification on the
generated calculation node trees. By doing that manually after parsing a
standalone math function, we score at least a couple hundred WPT points.
This commit is contained in:
Andreas Kling 2025-04-24 17:33:52 +02:00 committed by Andreas Kling
parent 94ae63c436
commit 0553bcb35b
Notes: github-actions[bot] 2025-04-24 18:38:57 +00:00
10 changed files with 1298 additions and 2 deletions

View file

@ -3515,8 +3515,11 @@ RefPtr<CalculationNode const> Parser::parse_a_calc_function_node(Function const&
if (function.name.equals_ignoring_ascii_case("calc"sv)) if (function.name.equals_ignoring_ascii_case("calc"sv))
return parse_a_calculation(function.value, context); return parse_a_calculation(function.value, context);
if (auto maybe_function = parse_math_function(function, context)) if (auto maybe_function = parse_math_function(function, context)) {
return maybe_function; // NOTE: We have to simplify manually here, since parse_math_function() is a helper for calc() parsing
// that doesn't do it directly by itself.
return simplify_a_calculation_tree(*maybe_function, context, CalculationResolutionContext {});
}
return nullptr; return nullptr;
} }

View file

@ -0,0 +1,22 @@
Harness status: OK
Found 17 tests
17 Pass
Pass Property letter-spacing value 'clamp(10px, 20px, 30px)'
Pass Property letter-spacing value 'clamp(10px, 5px, 30px)'
Pass Property letter-spacing value 'clamp(10px, 35px, 30px)'
Pass Property letter-spacing value 'clamp(10px, 35px , 30px)'
Pass Property letter-spacing value 'clamp(10px, 35px /*foo*/, 30px)'
Pass Property letter-spacing value 'clamp(10px /* foo */ , 35px, 30px)'
Pass Property letter-spacing value 'clamp(10px , 35px, 30px)'
Pass Property letter-spacing value 'clamp(30px, 100px, 20px)'
Pass Property letter-spacing value 'clamp(-30px, -20px, -10px)'
Pass Property letter-spacing value 'clamp(-30px, -100px, -10px)'
Pass Property letter-spacing value 'clamp(-30px, 100px, -10px)'
Pass Property letter-spacing value 'clamp(-10px, 100px, -30px)'
Pass Property letter-spacing value 'clamp(-10px, -100px, -30px)'
Pass Property letter-spacing value 'calc(0px + clamp(10px, 20px, 30px))'
Pass Property letter-spacing value 'calc(0px - clamp(10px, 20px, 30px))'
Pass Property letter-spacing value 'calc(0px + clamp(30px, 100px, 20px))'
Pass Property letter-spacing value 'calc(0px - clamp(30px, 100px, 20px))'

View file

@ -0,0 +1,37 @@
Harness status: OK
Found 32 tests
32 Pass
Pass min(1deg) should be used-value-equivalent to 1deg
Pass min(1grad) should be used-value-equivalent to 1grad
Pass min(1rad) should be used-value-equivalent to 1rad
Pass min(1turn) should be used-value-equivalent to 1turn
Pass max(1deg) should be used-value-equivalent to 1deg
Pass max(1grad) should be used-value-equivalent to 1grad
Pass max(1rad) should be used-value-equivalent to 1rad
Pass max(1turn) should be used-value-equivalent to 1turn
Pass min(1deg, 2deg) should be used-value-equivalent to 1deg
Pass min(1grad, 2grad) should be used-value-equivalent to 1grad
Pass min(1rad, 2rad) should be used-value-equivalent to 1rad
Pass min(1turn, 2turn) should be used-value-equivalent to 1turn
Pass max(1deg, 2deg) should be used-value-equivalent to 2deg
Pass max(1grad, 2grad) should be used-value-equivalent to 2grad
Pass max(1rad, 2rad) should be used-value-equivalent to 2rad
Pass max(1turn, 2turn) should be used-value-equivalent to 2turn
Pass min(90deg, 0.26turn) should be used-value-equivalent to 90deg
Pass min(1.57rad, 95deg) should be used-value-equivalent to 1.57rad
Pass max(91deg, 0.25turn) should be used-value-equivalent to 91deg
Pass max(1.58rad, 90deg) should be used-value-equivalent to 1.58rad
Pass min(270deg, max(0.25turn, 3.14rad)) should be used-value-equivalent to 3.14rad
Pass max(0.25turn, min(270deg, 3.14rad)) should be used-value-equivalent to 3.14rad
Pass calc(min(90deg, 1.58rad) + 0.125turn) should be used-value-equivalent to 135deg
Pass calc(min(90deg, 1.58rad) - 0.125turn) should be used-value-equivalent to 45deg
Pass calc(min(90deg, 1.58rad) * 1.5 should be used-value-equivalent to 135deg
Pass calc(min(90deg, 1.58rad) / 2 should be used-value-equivalent to 45deg
Pass calc(max(90deg, 1.56rad) + 0.125turn should be used-value-equivalent to 135deg
Pass calc(max(90deg, 1.56rad) - 0.125turn) should be used-value-equivalent to 45deg
Pass calc(max(90deg, 1.56rad) * 1.5 should be used-value-equivalent to 135deg
Pass calc(max(90deg, 1.56rad) / 2 should be used-value-equivalent to 45deg
Pass calc(min(90deg, 1.58rad) + max(0.125turn, 49grad)) should be used-value-equivalent to 135deg
Pass calc(min(90deg, 1.58rad) - max(0.25turn, 99grad)) should be used-value-equivalent to 0deg

View file

@ -0,0 +1,235 @@
Harness status: OK
Found 229 tests
195 Pass
34 Fail
Pass round(10,10) should be used-value-equivalent to 10
Pass mod(1,1) should be used-value-equivalent to 0
Pass rem(1,1) should be used-value-equivalent to 0
Pass calc(round(100,10)) should be used-value-equivalent to 100
Pass calc(round(up, 101,10)) should be used-value-equivalent to 110
Pass calc(round(down, 106,10)) should be used-value-equivalent to 100
Pass calc(round(to-zero, 105, 10)) should be used-value-equivalent to 100
Pass calc(round(to-zero, -105, 10)) should be used-value-equivalent to -100
Pass calc(round(-100, 10)) should be used-value-equivalent to -100
Pass calc(round(up, -103, 10)) should be used-value-equivalent to -100
Pass round(up, 0, 5) should be used-value-equivalent to 0
Pass round(down, 0, 5) should be used-value-equivalent to 0
Pass round(nearest, 0, 5) should be used-value-equivalent to 0
Pass round(to-zero, 0, 5) should be used-value-equivalent to 0
Pass round(up, 5, 5) should be used-value-equivalent to 5
Pass round(down, 5, 5) should be used-value-equivalent to 5
Pass round(nearest, 5, 5) should be used-value-equivalent to 5
Pass round(to-zero, 5, 5) should be used-value-equivalent to 5
Pass round(up, -5, 5) should be used-value-equivalent to -5
Pass round(down, -5, 5) should be used-value-equivalent to -5
Pass round(nearest, -5, 5) should be used-value-equivalent to -5
Pass round(to-zero, -5, 5) should be used-value-equivalent to -5
Pass round(up, 10, 5) should be used-value-equivalent to 10
Pass round(down, 10, 5) should be used-value-equivalent to 10
Pass round(nearest, 10, 5) should be used-value-equivalent to 10
Pass round(to-zero, 10, 5) should be used-value-equivalent to 10
Pass round(up, -10, 5) should be used-value-equivalent to -10
Pass round(down, -10, 5) should be used-value-equivalent to -10
Pass round(nearest, -10, 5) should be used-value-equivalent to -10
Pass round(to-zero, -10, 5) should be used-value-equivalent to -10
Pass round(up, 20, 5) should be used-value-equivalent to 20
Pass round(down, 20, 5) should be used-value-equivalent to 20
Pass round(nearest, 20, 5) should be used-value-equivalent to 20
Pass round(to-zero, 20, 5) should be used-value-equivalent to 20
Pass round(up, -20, 5) should be used-value-equivalent to -20
Pass round(down, -20, 5) should be used-value-equivalent to -20
Pass round(nearest, -20, 5) should be used-value-equivalent to -20
Pass round(to-zero, -20, 5) should be used-value-equivalent to -20
Pass mod(18,5) should be used-value-equivalent to 3
Pass rem(18,5) should be used-value-equivalent to 3
Pass mod(-140,-90) should be used-value-equivalent to -50
Pass mod(-18,5) should be used-value-equivalent to 2
Pass rem(-18,5) should be used-value-equivalent to -3
Pass mod(140,-90) should be used-value-equivalent to -40
Pass rem(140,-90) should be used-value-equivalent to 50
Pass calc(round(round(100,10), 10)) should be used-value-equivalent to 100
Pass calc(round(up, round(100,10) + 1,10)) should be used-value-equivalent to 110
Pass calc(round(down, round(100,10) + 2 * 3,10)) should be used-value-equivalent to 100
Pass calc(round(to-zero,round(100,10) * 2 - 95, 10)) should be used-value-equivalent to 100
Pass calc(round(round(100,10)* -1,10)) should be used-value-equivalent to -100
Pass calc(round(up, -103 + -103 / -103 - 1,10)) should be used-value-equivalent to -100
Pass calc(mod(18,5) * 2 + mod(17,5)) should be used-value-equivalent to 8
Pass calc(rem(mod(18,5),5)) should be used-value-equivalent to 3
Pass calc(rem(mod(18,5),mod(17,5))) should be used-value-equivalent to 1
Pass calc(mod(-140,-90)) should be used-value-equivalent to -50
Pass calc(mod(rem(1,18)* -1,5)) should be used-value-equivalent to 4
Pass round(10px,6px) should be used-value-equivalent to 12px
Fail round(10cm,6cm) should be used-value-equivalent to 12cm
Fail round(10mm,6mm) should be used-value-equivalent to 12mm
Pass round(10Q, 6Q) should be used-value-equivalent to 12Q
Pass round(10in,6in) should be used-value-equivalent to 12in
Pass round(10pc,6pc) should be used-value-equivalent to 12pc
Pass round(10pt,6pt) should be used-value-equivalent to 12pt
Pass round(10em,6em) should be used-value-equivalent to 12em
Pass round(10ex,6ex) should be used-value-equivalent to 12ex
Pass round(10ch,6ch) should be used-value-equivalent to 12ch
Pass round(10rem,6rem) should be used-value-equivalent to 12rem
Pass round(10vh,6vh) should be used-value-equivalent to 12vh
Pass round(10vw,6vw) should be used-value-equivalent to 12vw
Pass round(10vmin,6vmin) should be used-value-equivalent to 12vmin
Pass round(10vmax,6vmax) should be used-value-equivalent to 12vmax
Pass round(10s,6s) should be used-value-equivalent to 12s
Pass round(10ms,6ms) should be used-value-equivalent to 12ms
Pass round(10deg,6deg) should be used-value-equivalent to 12deg
Pass round(10grad,6grad) should be used-value-equivalent to 12grad
Pass round(10rad,6rad) should be used-value-equivalent to 12rad
Pass round(10turn,6turn) should be used-value-equivalent to 12turn
Pass mod(10px,6px) should be used-value-equivalent to 4px
Pass mod(10cm,6cm) should be used-value-equivalent to 4cm
Pass mod(10mm,6mm) should be used-value-equivalent to 4mm
Pass mod(10Q, 6Q) should be used-value-equivalent to 4Q
Pass mod(10in,6in) should be used-value-equivalent to 4in
Pass mod(10pc,6pc) should be used-value-equivalent to 4pc
Pass mod(10em,6em) should be used-value-equivalent to 4em
Pass mod(10ex,6ex) should be used-value-equivalent to 4ex
Pass mod(10ch,6ch) should be used-value-equivalent to 4ch
Pass mod(10rem,6rem) should be used-value-equivalent to 4rem
Pass mod(10vh,6vh) should be used-value-equivalent to 4vh
Pass mod(10vw,6vw) should be used-value-equivalent to 4vw
Pass mod(10vmin,6vmin) should be used-value-equivalent to 4vmin
Pass mod(10vmax,6vmax) should be used-value-equivalent to 4vmax
Pass mod(10s,6s) should be used-value-equivalent to 4s
Pass mod(10ms,6ms) should be used-value-equivalent to 4ms
Pass mod(10deg,6deg) should be used-value-equivalent to 4deg
Pass mod(10grad,6grad) should be used-value-equivalent to 4grad
Pass mod(10rad,6rad) should be used-value-equivalent to 4rad
Pass mod(10turn,6turn) should be used-value-equivalent to 4turn
Pass rem(10px,6px) should be used-value-equivalent to 4px
Pass rem(10cm,6cm) should be used-value-equivalent to 4cm
Pass rem(10mm,6mm) should be used-value-equivalent to 4mm
Pass rem(10Q, 6Q) should be used-value-equivalent to 4Q
Pass rem(10in,6in) should be used-value-equivalent to 4in
Pass rem(10pc,6pc) should be used-value-equivalent to 4pc
Pass rem(10em,6em) should be used-value-equivalent to 4em
Pass rem(10ex,6ex) should be used-value-equivalent to 4ex
Pass rem(10ch,6ch) should be used-value-equivalent to 4ch
Pass rem(10rem,6rem) should be used-value-equivalent to 4rem
Pass rem(10vh,6vh) should be used-value-equivalent to 4vh
Pass rem(10vw,6vw) should be used-value-equivalent to 4vw
Pass rem(10vmin,6vmin) should be used-value-equivalent to 4vmin
Pass rem(10vmax,6vmax) should be used-value-equivalent to 4vmax
Pass rem(10s,6s) should be used-value-equivalent to 4s
Pass rem(10ms,6ms) should be used-value-equivalent to 4ms
Pass rem(10deg,6deg) should be used-value-equivalent to 4deg
Pass rem(10grad,6grad) should be used-value-equivalent to 4grad
Pass rem(10rad,6rad) should be used-value-equivalent to 4rad
Pass rem(10turn,6turn) should be used-value-equivalent to 4turn
Fail round(10%,1px) should be used-value-equivalent to 8px
Fail round(10%,5px) should be used-value-equivalent to 10px
Pass round(2rem,5px) should be used-value-equivalent to 30px
Pass round(100px,1rem) should be used-value-equivalent to 96px
Pass round(10s,6000ms) should be used-value-equivalent to 12s
Pass round(10000ms,6s) should be used-value-equivalent to 12s
Fail mod(10%,1px) should be used-value-equivalent to 0.5px
Fail mod(10%,5px) should be used-value-equivalent to 2.5px
Pass mod(2rem,5px) should be used-value-equivalent to 2px
Pass mod(100px,1rem) should be used-value-equivalent to 4px
Pass mod(10s,6000ms) should be used-value-equivalent to 4s
Pass mod(10000ms,6s) should be used-value-equivalent to 4s
Fail mod(18px,100% / 15) should be used-value-equivalent to 3px
Fail mod(-18px,100% / 10) should be used-value-equivalent to 4.5px
Pass mod(18%,5%) should be used-value-equivalent to 3%
Pass mod(-19%,5%) should be used-value-equivalent to 1%
Pass mod(18vw,5vw) should be used-value-equivalent to 3vw
Pass mod(-18vw,5vw) should be used-value-equivalent to 2vw
Fail rem(10%,1px) should be used-value-equivalent to 0.5px
Fail rem(10%,5px) should be used-value-equivalent to 2.5px
Pass rem(2rem,5px) should be used-value-equivalent to 2px
Pass rem(100px,1rem) should be used-value-equivalent to 4px
Pass rem(10s,6000ms) should be used-value-equivalent to 4s
Pass rem(10000ms,6s) should be used-value-equivalent to 4s
Fail rem(18px,100% / 15) should be used-value-equivalent to 3px
Fail rem(-18px,100% / 15) should be used-value-equivalent to -3px
Pass rem(18vw,5vw) should be used-value-equivalent to 3vw
Pass rem(-18vw,5vw) should be used-value-equivalent to -3vw
Pass calc(round(1px + 0%, 1px + 0%)) should be used-value-equivalent to 1px
Pass calc(mod(3px + 0%, 2px + 0%)) should be used-value-equivalent to 1px
Pass calc(rem(3px + 0%, 2px + 0%)) should be used-value-equivalent to 1px
Pass round(1px + 0%, 1px) should be used-value-equivalent to 1px
Fail mod(3px + 0%, 2px) should be used-value-equivalent to 1px
Fail rem(3px + 0%, 2px) should be used-value-equivalent to 1px
Pass round(0, 0) should be used-value-equivalent to calc(NaN)
Pass round(-0, 0) should be used-value-equivalent to calc(NaN)
Pass round(Infinity, 0) should be used-value-equivalent to calc(NaN)
Pass round(-Infinity, 0) should be used-value-equivalent to calc(NaN)
Pass round(-4, 0) should be used-value-equivalent to calc(NaN)
Pass round(4, 0) should be used-value-equivalent to calc(NaN)
Pass round(Infinity, Infinity) should be used-value-equivalent to calc(NaN)
Pass round(-Infinity, -Infinity) should be used-value-equivalent to calc(NaN)
Pass round(Infinity, -Infinity) should be used-value-equivalent to calc(NaN)
Pass round(-Infinity, Infinity) should be used-value-equivalent to calc(NaN)
Pass mod(0, 0) should be used-value-equivalent to calc(NaN)
Pass mod(-0, 0) should be used-value-equivalent to calc(NaN)
Pass mod(Infinity, 0) should be used-value-equivalent to calc(NaN)
Pass mod(-Infinity, 0) should be used-value-equivalent to calc(NaN)
Pass mod(-4, 0) should be used-value-equivalent to calc(NaN)
Pass mod(4, 0) should be used-value-equivalent to calc(NaN)
Pass mod(Infinity, Infinity) should be used-value-equivalent to calc(NaN)
Pass mod(-Infinity, -Infinity) should be used-value-equivalent to calc(NaN)
Pass mod(Infinity, -Infinity) should be used-value-equivalent to calc(NaN)
Pass mod(-Infinity, Infinity) should be used-value-equivalent to calc(NaN)
Pass rem(0, 0) should be used-value-equivalent to calc(NaN)
Pass rem(-0, 0) should be used-value-equivalent to calc(NaN)
Pass rem(Infinity, 0) should be used-value-equivalent to calc(NaN)
Pass rem(-Infinity, 0) should be used-value-equivalent to calc(NaN)
Pass rem(-4, 0) should be used-value-equivalent to calc(NaN)
Pass rem(4, 0) should be used-value-equivalent to calc(NaN)
Pass rem(Infinity, Infinity) should be used-value-equivalent to calc(NaN)
Pass rem(-Infinity, -Infinity) should be used-value-equivalent to calc(NaN)
Pass rem(Infinity, -Infinity) should be used-value-equivalent to calc(NaN)
Pass rem(-Infinity, Infinity) should be used-value-equivalent to calc(NaN)
Pass round(up, Infinity, 4) should be used-value-equivalent to calc(Infinity)
Pass round(up, -Infinity, 4) should be used-value-equivalent to calc(-Infinity)
Pass round(up, Infinity, -4) should be used-value-equivalent to calc(Infinity)
Pass round(up, -Infinity, -4) should be used-value-equivalent to calc(-Infinity)
Pass round(down, Infinity, 4) should be used-value-equivalent to calc(Infinity)
Pass round(down, -Infinity, 4) should be used-value-equivalent to calc(-Infinity)
Pass round(down, Infinity, -4) should be used-value-equivalent to calc(Infinity)
Pass round(down, -Infinity, -4) should be used-value-equivalent to calc(-Infinity)
Pass round(nearest, Infinity, 4) should be used-value-equivalent to calc(Infinity)
Pass round(nearest, -Infinity, 4) should be used-value-equivalent to calc(-Infinity)
Pass round(nearest, Infinity, -4) should be used-value-equivalent to calc(Infinity)
Pass round(nearest, -Infinity, -4) should be used-value-equivalent to calc(-Infinity)
Pass round(to-zero, Infinity, 4) should be used-value-equivalent to calc(Infinity)
Pass round(to-zero, -Infinity, 4) should be used-value-equivalent to calc(-Infinity)
Pass round(to-zero, Infinity, -4) should be used-value-equivalent to calc(Infinity)
Pass round(to-zero, -Infinity, -4) should be used-value-equivalent to calc(-Infinity)
Pass round(nearest, 0, Infinity) should be used-value-equivalent to 0
Fail round(nearest, 4, Infinity) should be used-value-equivalent to 0
Fail round(nearest, -0, Infinity) should be used-value-equivalent to calc(-0)
Fail round(nearest, -4, Infinity) should be used-value-equivalent to calc(-0)
Pass round(nearest, 0, -Infinity) should be used-value-equivalent to 0
Fail round(nearest, 4, -Infinity) should be used-value-equivalent to 0
Fail round(nearest, -0, -Infinity) should be used-value-equivalent to calc(-0)
Fail round(nearest, -4, -Infinity) should be used-value-equivalent to calc(-0)
Pass round(to-zero, 0, Infinity) should be used-value-equivalent to 0
Fail round(to-zero, 4, Infinity) should be used-value-equivalent to 0
Fail round(to-zero, -0, Infinity) should be used-value-equivalent to calc(-0)
Fail round(to-zero, -4, Infinity) should be used-value-equivalent to calc(-0)
Pass round(to-zero, 0, -Infinity) should be used-value-equivalent to 0
Fail round(to-zero, 4, -Infinity) should be used-value-equivalent to 0
Fail round(to-zero, -0, -Infinity) should be used-value-equivalent to calc(-0)
Fail round(to-zero, -4, -Infinity) should be used-value-equivalent to calc(-0)
Fail round(up, 1, Infinity) should be used-value-equivalent to calc(Infinity)
Pass round(up, 0, Infinity) should be used-value-equivalent to 0
Fail round(up, -1, Infinity) should be used-value-equivalent to calc(-0)
Fail round(up, 1, -Infinity) should be used-value-equivalent to calc(Infinity)
Pass round(up, 0, -Infinity) should be used-value-equivalent to 0
Fail round(up, -1, -Infinity) should be used-value-equivalent to calc(-0)
Fail round(down, 1, Infinity) should be used-value-equivalent to calc(-0)
Pass round(down, 0, Infinity) should be used-value-equivalent to 0
Fail round(down, -1, Infinity) should be used-value-equivalent to calc(-Infinity)
Fail round(down, 1, -Infinity) should be used-value-equivalent to calc(-0)
Pass round(down, 0, -Infinity) should be used-value-equivalent to 0
Fail round(down, -1, -Infinity) should be used-value-equivalent to calc(-Infinity)
Pass mod(-0, Infinity) should be used-value-equivalent to calc(NaN)
Pass mod(0, -Infinity) should be used-value-equivalent to calc(NaN)
Pass mod(-4, Infinity) should be used-value-equivalent to calc(NaN)
Pass mod(4, -Infinity) should be used-value-equivalent to calc(NaN)

View file

@ -0,0 +1,239 @@
Harness status: OK
Found 233 tests
120 Pass
113 Fail
Pass abs(1) should be used-value-equivalent to 1
Pass sign(1) should be used-value-equivalent to 1
Pass abs(-1) should be used-value-equivalent to 1
Pass sign(-1) should be used-value-equivalent to -1
Pass abs(sign(1)) should be used-value-equivalent to 1
Pass abs(sign(sign(1))) should be used-value-equivalent to 1
Pass sign(sign(sign(1) + sign(1))) should be used-value-equivalent to 1
Pass calc(abs(0.1 + 0.2) + 0.05) should be used-value-equivalent to 0.35
Pass calc(sign(0.1 + 0.2) - 0.05) should be used-value-equivalent to 0.95
Pass calc(abs(0.1 + 0.2) * 2) should be used-value-equivalent to 0.6
Pass calc(abs(sign(0.1) + 0.2) / 2) should be used-value-equivalent to 0.6
Pass calc(abs(0.1 + 0.2) * -2) should be used-value-equivalent to -0.6
Pass calc(sign(0.1 - 0.2) - 0.05) should be used-value-equivalent to -1.05
Pass calc(sign(1) + sign(1) - 0.05) should be used-value-equivalent to 1.95
Pass abs(10px) should be used-value-equivalent to 10px
Fail abs(10%) should be used-value-equivalent to 10px
Fail abs(10px + 10%) should be used-value-equivalent to 20px
Fail calc(10px + abs(10%)) should be used-value-equivalent to 20px
Pass abs(-10px) should be used-value-equivalent to 10px
Fail abs(-10%) should be used-value-equivalent to 10px
Fail calc((1em + 1px) * (sign(1em - 10px - 10%) + 1)) should be used-value-equivalent to 21px
Pass calc(calc(sign(-0))) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(calc(sign(-0)))), 1) should be used-value-equivalent to -1
Pass calc(calc(sign(0))) should be used-value-equivalent to 0
Pass clamp(-1, calc( 1 / sign(calc(sign(0)))), 1) should be used-value-equivalent to 1
Pass abs(infinity) should be used-value-equivalent to calc(infinity)
Pass abs(-infinity) should be used-value-equivalent to calc(infinity)
Pass abs(NaN) should be used-value-equivalent to calc(NaN)
Pass calc(20 - abs(-10)) should be used-value-equivalent to 10
Pass calc(20 - abs(10)) should be used-value-equivalent to 10
Pass calc(10 - abs(10 - abs(-30)) should be used-value-equivalent to -10
Pass calc(2 - sign(1)) should be used-value-equivalent to 1
Pass calc(2 - sign(-1)) should be used-value-equivalent to 3
Pass calc(2 - sign(1 - sign(-1))) should be used-value-equivalent to 1
Pass calc(10 - abs(20 - sign(2 - abs(-20)))) should be used-value-equivalent to -11
Pass sign(1px) should be used-value-equivalent to 1
Pass sign(1cm) should be used-value-equivalent to 1
Pass sign(1mm) should be used-value-equivalent to 1
Pass sign(1Q) should be used-value-equivalent to 1
Pass sign(1in) should be used-value-equivalent to 1
Pass sign(1pc) should be used-value-equivalent to 1
Pass sign(1pt) should be used-value-equivalent to 1
Fail sign(1em) should be used-value-equivalent to 1
Fail sign(1ex) should be used-value-equivalent to 1
Fail sign(1ch) should be used-value-equivalent to 1
Fail sign(1rem) should be used-value-equivalent to 1
Fail sign(1vh) should be used-value-equivalent to 1
Fail sign(1vw) should be used-value-equivalent to 1
Fail sign(1vmin) should be used-value-equivalent to 1
Fail sign(1vmax) should be used-value-equivalent to 1
Pass sign(-1px) should be used-value-equivalent to -1
Pass sign(-1cm) should be used-value-equivalent to -1
Pass sign(-1mm) should be used-value-equivalent to -1
Pass sign(-1Q) should be used-value-equivalent to -1
Pass sign(-1in) should be used-value-equivalent to -1
Pass sign(-1pc) should be used-value-equivalent to -1
Pass sign(-1pt) should be used-value-equivalent to -1
Fail sign(-1em) should be used-value-equivalent to -1
Fail sign(-1ex) should be used-value-equivalent to -1
Fail sign(-1ch) should be used-value-equivalent to -1
Fail sign(-1rem) should be used-value-equivalent to -1
Fail sign(-1vh) should be used-value-equivalent to -1
Fail sign(-1vw) should be used-value-equivalent to -1
Fail sign(-1vmin) should be used-value-equivalent to -1
Fail sign(-1vmax) should be used-value-equivalent to -1
Pass sign(1s) should be used-value-equivalent to 1
Pass sign(1ms) should be used-value-equivalent to 1
Pass sign(-1s) should be used-value-equivalent to -1
Pass sign(-1ms) should be used-value-equivalent to -1
Pass sign(1deg) should be used-value-equivalent to 1
Pass sign(1grad) should be used-value-equivalent to 1
Pass sign(1rad) should be used-value-equivalent to 1
Pass sign(1turn) should be used-value-equivalent to 1
Pass sign(-1deg) should be used-value-equivalent to -1
Pass sign(-1grad) should be used-value-equivalent to -1
Pass sign(-1rad) should be used-value-equivalent to -1
Pass sign(-1turn) should be used-value-equivalent to -1
Pass calc(sign(0px)) should be used-value-equivalent to 0
Pass clamp(-1, calc( 1 / sign(sign(0px))), 1) should be used-value-equivalent to 1
Pass calc(sign(0cm)) should be used-value-equivalent to 0
Pass clamp(-1, calc( 1 / sign(sign(0cm))), 1) should be used-value-equivalent to 1
Pass calc(sign(0mm)) should be used-value-equivalent to 0
Pass clamp(-1, calc( 1 / sign(sign(0mm))), 1) should be used-value-equivalent to 1
Pass calc(sign(0Q)) should be used-value-equivalent to 0
Pass clamp(-1, calc( 1 / sign(sign(0Q))), 1) should be used-value-equivalent to 1
Pass calc(sign(0in)) should be used-value-equivalent to 0
Pass clamp(-1, calc( 1 / sign(sign(0in))), 1) should be used-value-equivalent to 1
Pass calc(sign(0pc)) should be used-value-equivalent to 0
Pass clamp(-1, calc( 1 / sign(sign(0pc))), 1) should be used-value-equivalent to 1
Pass calc(sign(0pt)) should be used-value-equivalent to 0
Pass clamp(-1, calc( 1 / sign(sign(0pt))), 1) should be used-value-equivalent to 1
Fail calc(sign(0em)) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(sign(0em))), 1) should be used-value-equivalent to 1
Fail calc(sign(0ex)) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(sign(0ex))), 1) should be used-value-equivalent to 1
Fail calc(sign(0ch)) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(sign(0ch))), 1) should be used-value-equivalent to 1
Fail calc(sign(0rem)) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(sign(0rem))), 1) should be used-value-equivalent to 1
Fail calc(sign(0vh)) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(sign(0vh))), 1) should be used-value-equivalent to 1
Fail calc(sign(0vw)) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(sign(0vw))), 1) should be used-value-equivalent to 1
Fail calc(sign(0vmin)) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(sign(0vmin))), 1) should be used-value-equivalent to 1
Fail calc(sign(0vmax)) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(sign(0vmax))), 1) should be used-value-equivalent to 1
Pass calc(sign(-0px)) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(sign(-0px))), 1) should be used-value-equivalent to -1
Pass calc(sign(-0cm)) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(sign(-0cm))), 1) should be used-value-equivalent to -1
Pass calc(sign(-0mm)) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(sign(-0mm))), 1) should be used-value-equivalent to -1
Pass calc(sign(-0Q)) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(sign(-0Q))), 1) should be used-value-equivalent to -1
Pass calc(sign(-0in)) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(sign(-0in))), 1) should be used-value-equivalent to -1
Pass calc(sign(-0pc)) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(sign(-0pc))), 1) should be used-value-equivalent to -1
Pass calc(sign(-0pt)) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(sign(-0pt))), 1) should be used-value-equivalent to -1
Fail calc(sign(-0em)) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(sign(-0em))), 1) should be used-value-equivalent to -1
Fail calc(sign(-0ex)) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(sign(-0ex))), 1) should be used-value-equivalent to -1
Fail calc(sign(-0ch)) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(sign(-0ch))), 1) should be used-value-equivalent to -1
Fail calc(sign(-0rem)) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(sign(-0rem))), 1) should be used-value-equivalent to -1
Fail calc(sign(-0vh)) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(sign(-0vh))), 1) should be used-value-equivalent to -1
Fail calc(sign(-0vw)) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(sign(-0vw))), 1) should be used-value-equivalent to -1
Fail calc(sign(-0vmin)) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(sign(-0vmin))), 1) should be used-value-equivalent to -1
Fail calc(sign(-0vmax)) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(sign(-0vmax))), 1) should be used-value-equivalent to -1
Pass calc(sign(0s)) should be used-value-equivalent to 0
Pass clamp(-1, calc( 1 / sign(sign(0s))), 1) should be used-value-equivalent to 1
Pass calc(sign(0ms)) should be used-value-equivalent to 0
Pass clamp(-1, calc( 1 / sign(sign(0ms))), 1) should be used-value-equivalent to 1
Pass calc(sign(-0s)) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(sign(-0s))), 1) should be used-value-equivalent to -1
Pass calc(sign(-0ms)) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(sign(-0ms))), 1) should be used-value-equivalent to -1
Pass calc(sign(0deg)) should be used-value-equivalent to 0
Pass clamp(-1, calc( 1 / sign(sign(0deg))), 1) should be used-value-equivalent to 1
Pass calc(sign(0grad)) should be used-value-equivalent to 0
Pass clamp(-1, calc( 1 / sign(sign(0grad))), 1) should be used-value-equivalent to 1
Pass calc(sign(0rad)) should be used-value-equivalent to 0
Pass clamp(-1, calc( 1 / sign(sign(0rad))), 1) should be used-value-equivalent to 1
Pass calc(sign(0turn)) should be used-value-equivalent to 0
Pass clamp(-1, calc( 1 / sign(sign(0turn))), 1) should be used-value-equivalent to 1
Pass calc(sign(-0deg)) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(sign(-0deg))), 1) should be used-value-equivalent to -1
Pass calc(sign(-0grad)) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(sign(-0grad))), 1) should be used-value-equivalent to -1
Pass calc(sign(-0rad)) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(sign(-0rad))), 1) should be used-value-equivalent to -1
Pass calc(sign(-0turn)) should be used-value-equivalent to 0
Fail clamp(-1, calc( 1 / sign(sign(-0turn))), 1) should be used-value-equivalent to -1
Pass abs(1px) should be used-value-equivalent to 1px
Pass abs(1cm) should be used-value-equivalent to 1cm
Pass abs(1mm) should be used-value-equivalent to 1mm
Pass abs(1Q) should be used-value-equivalent to 1Q
Pass abs(1in) should be used-value-equivalent to 1in
Pass abs(1pc) should be used-value-equivalent to 1pc
Pass abs(1pt) should be used-value-equivalent to 1pt
Fail abs(1em) should be used-value-equivalent to 1em
Fail abs(1ex) should be used-value-equivalent to 1ex
Fail abs(1ch) should be used-value-equivalent to 1ch
Fail abs(1rem) should be used-value-equivalent to 1rem
Fail abs(1vh) should be used-value-equivalent to 1vh
Fail abs(1vw) should be used-value-equivalent to 1vw
Fail abs(1vmin) should be used-value-equivalent to 1vmin
Fail abs(1vmax) should be used-value-equivalent to 1vmax
Pass abs(-1px) should be used-value-equivalent to 1px
Pass abs(-1cm) should be used-value-equivalent to 1cm
Pass abs(-1mm) should be used-value-equivalent to 1mm
Pass abs(-1Q) should be used-value-equivalent to 1Q
Pass abs(-1in) should be used-value-equivalent to 1in
Pass abs(-1pc) should be used-value-equivalent to 1pc
Pass abs(-1pt) should be used-value-equivalent to 1pt
Fail abs(-1em) should be used-value-equivalent to 1em
Fail abs(-1ex) should be used-value-equivalent to 1ex
Fail abs(-1ch) should be used-value-equivalent to 1ch
Fail abs(-1rem) should be used-value-equivalent to 1rem
Fail abs(-1vh) should be used-value-equivalent to 1vh
Fail abs(-1vw) should be used-value-equivalent to 1vw
Fail abs(-1vmin) should be used-value-equivalent to 1vmin
Fail abs(-1vmax) should be used-value-equivalent to 1vmax
Pass abs(1s) should be used-value-equivalent to 1s
Pass abs(1ms) should be used-value-equivalent to 1ms
Pass abs(-1s) should be used-value-equivalent to 1s
Pass abs(-1ms) should be used-value-equivalent to 1ms
Pass abs(1deg) should be used-value-equivalent to 1deg
Pass abs(1grad) should be used-value-equivalent to 1grad
Pass abs(1rad) should be used-value-equivalent to 1rad
Pass abs(1turn) should be used-value-equivalent to 1turn
Pass abs(-1deg) should be used-value-equivalent to 1deg
Pass abs(-1grad) should be used-value-equivalent to 1grad
Pass abs(-1rad) should be used-value-equivalent to 1rad
Pass abs(-1turn) should be used-value-equivalent to 1turn
Fail sign(10px - 1em) should be used-value-equivalent to 0; fontSize=10px
Fail sign(10px - 2em) should be used-value-equivalent to -1; fontSize=10px
Fail calc(sign(10%) * 100px) should be used-value-equivalent to 100px
Fail calc(2.5 - sign(41px - 2em) / 2) should be used-value-equivalent to 2
Fail calc(2.5 - sign(40px - 2em) / 2) should be used-value-equivalent to 2.5
Fail calc(2.5 - sign(39px - 2em) / 2) should be used-value-equivalent to 3
Fail calc(3 + sign(42px - 2em)) should be used-value-equivalent to 4
Fail calc(3 + sign(40px - 2em)) should be used-value-equivalent to 3
Fail calc(3 + sign(38px - 2em)) should be used-value-equivalent to 2
Fail calc(50px + 100px * sign(42px - 2em)) should be used-value-equivalent to 150px
Fail calc(50px + 100px * sign(40px - 2em)) should be used-value-equivalent to 50px
Fail calc(50px + 100px * sign(38px - 2em)) should be used-value-equivalent to -50px
Fail calc(90deg + 30deg * sign(42px - 2em)) should be used-value-equivalent to 120deg
Fail calc(90deg + 30deg * sign(40px - 2em)) should be used-value-equivalent to 90deg
Fail calc(90deg + 30deg * sign(38px - 2em)) should be used-value-equivalent to 60deg
Fail calc(5s + 15s * sign(42px - 2em)) should be used-value-equivalent to 20s
Fail calc(5s + 15s * sign(40px - 2em)) should be used-value-equivalent to 5s
Fail calc(5s + 15s * sign(38px - 2em)) should be used-value-equivalent to -10s
Fail calc(100dpi + 20dpi * sign(42px - 2em)) should be used-value-equivalent to 120dpi
Fail calc(100dpi + 20dpi * sign(40px - 2em)) should be used-value-equivalent to 100dpi
Fail calc(100dpi + 20dpi * sign(38px - 2em)) should be used-value-equivalent to 80dpi
Fail calc(3fr + 1fr * sign(42px - 2em)) should be used-value-equivalent to 4fr
Fail calc(3fr + 1fr * sign(40px - 2em)) should be used-value-equivalent to 3fr
Fail calc(3fr + 1fr * sign(38px - 2em)) should be used-value-equivalent to 2fr
Fail calc(2.5 - sign(33px - 2rem) / 2) should be used-value-equivalent to 2
Fail calc(2.5 - sign(32px - 2rem) / 2) should be used-value-equivalent to 2.5
Fail calc(2.5 - sign(31px - 2rem) / 2) should be used-value-equivalent to 3
Fail calc(50px + 100px * sign(34px - 2rem)) should be used-value-equivalent to 150px
Fail calc(50px + 100px * sign(32px - 2rem)) should be used-value-equivalent to 50px
Fail calc(50px + 100px * sign(30px - 2rem)) should be used-value-equivalent to -50px

View file

@ -0,0 +1,47 @@
<!DOCTYPE html>
<link rel="help" href="https://drafts.csswg.org/css-values-4/#comp-func">
<link rel="author" title="Xiaocheng Hu" href="mailto:xiaochengh@chromium.org">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="../support/computed-testcommon.js"></script>
<div id="container" style="font-size: 20px">
<div id="target"></div>
<div id="reference"></div>
</div>
<script>
const property = 'letter-spacing';
function test_length_equals(value, expected) {
const reference = document.getElementById('reference');
reference.style[property] = '';
reference.style[property] = expected;
const computed = getComputedStyle(reference)[property];
test_computed_value(property, value, computed);
}
test_length_equals('clamp(10px, 20px, 30px)', '20px');
test_length_equals('clamp(10px, 5px, 30px)', '10px');
test_length_equals('clamp(10px, 35px, 30px)', '30px');
test_length_equals('clamp(10px, 35px , 30px)', '30px');
test_length_equals('clamp(10px, 35px /*foo*/, 30px)', '30px');
test_length_equals('clamp(10px /* foo */ , 35px, 30px)', '30px');
test_length_equals('clamp(10px , 35px, 30px)', '30px');
// clamp(MIN, VAL, MAX) is identical to max(MIN, min(VAL, MAX)),
// so MIN wins over MAX if they are in the wrong order.
test_length_equals('clamp(30px, 100px, 20px)', '30px');
// also test with negative values
test_length_equals('clamp(-30px, -20px, -10px)', '-20px');
test_length_equals('clamp(-30px, -100px, -10px)', '-30px');
test_length_equals('clamp(-30px, 100px, -10px)', '-10px');
test_length_equals('clamp(-10px, 100px, -30px)', '-10px');
test_length_equals('clamp(-10px, -100px, -30px)', '-10px');
// and negating the result of clamp
test_length_equals('calc(0px + clamp(10px, 20px, 30px))', '20px');
test_length_equals('calc(0px - clamp(10px, 20px, 30px))', '-20px');
test_length_equals('calc(0px + clamp(30px, 100px, 20px))', '30px');
test_length_equals('calc(0px - clamp(30px, 100px, 20px))', '-30px');
</script>

View file

@ -0,0 +1,57 @@
<!DOCTYPE html>
<link rel="help" href="https://drafts.csswg.org/css-values-4/#comp-func">
<link rel="help" href="https://drafts.csswg.org/css-values-4/#angles">
<link rel="help" href="https://drafts.csswg.org/css-values-4/#calc-type-checking">
<link rel="author" title="Xiaocheng Hu" href="mailto:xiaochengh@chromium.org">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="../support/numeric-testcommon.js"></script>
<div id="target"></div>
<div id="reference"></div>
<script>
function test_angle_equals(value, expected) {
test_math_used(value, expected, {type: "angle"});
}
// Identity tests
test_angle_equals('min(1deg)', '1deg');
test_angle_equals('min(1grad)', '1grad');
test_angle_equals('min(1rad)', '1rad');
test_angle_equals('min(1turn)', '1turn');
test_angle_equals('max(1deg)', '1deg');
test_angle_equals('max(1grad)', '1grad');
test_angle_equals('max(1rad)', '1rad');
test_angle_equals('max(1turn)', '1turn');
// Comparisons between same units
test_angle_equals('min(1deg, 2deg)', '1deg');
test_angle_equals('min(1grad, 2grad)', '1grad');
test_angle_equals('min(1rad, 2rad)', '1rad');
test_angle_equals('min(1turn, 2turn)', '1turn');
test_angle_equals('max(1deg, 2deg)', '2deg');
test_angle_equals('max(1grad, 2grad)', '2grad');
test_angle_equals('max(1rad, 2rad)', '2rad');
test_angle_equals('max(1turn, 2turn)', '2turn');
// Comparisons between different units
test_angle_equals('min(90deg, 0.26turn)', '90deg');
test_angle_equals('min(1.57rad, 95deg)', '1.57rad');
test_angle_equals('max(91deg, 0.25turn)', '91deg');
test_angle_equals('max(1.58rad, 90deg)', '1.58rad');
// Nestings
test_angle_equals('min(270deg, max(0.25turn, 3.14rad))', '3.14rad');
test_angle_equals('max(0.25turn, min(270deg, 3.14rad))', '3.14rad');
// General calculations
test_angle_equals('calc(min(90deg, 1.58rad) + 0.125turn)', '135deg');
test_angle_equals('calc(min(90deg, 1.58rad) - 0.125turn)', '45deg');
test_angle_equals('calc(min(90deg, 1.58rad) * 1.5', '135deg');
test_angle_equals('calc(min(90deg, 1.58rad) / 2', '45deg');
test_angle_equals('calc(max(90deg, 1.56rad) + 0.125turn', '135deg');
test_angle_equals('calc(max(90deg, 1.56rad) - 0.125turn)', '45deg');
test_angle_equals('calc(max(90deg, 1.56rad) * 1.5', '135deg');
test_angle_equals('calc(max(90deg, 1.56rad) / 2', '45deg');
test_angle_equals('calc(min(90deg, 1.58rad) + max(0.125turn, 49grad))', '135deg');
test_angle_equals('calc(min(90deg, 1.58rad) - max(0.25turn, 99grad))', '0deg');
</script>

View file

@ -0,0 +1,218 @@
<!DOCTYPE html>
<link rel="help" href="https://drafts.csswg.org/css-values-4/#round-func">
<link rel="help" href="https://drafts.csswg.org/css-values-4/#numbers">
<link rel="help" href="https://drafts.csswg.org/css-values-4/#calc-type-checking">
<link rel="author" title="Apple Inc">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="../support/numeric-testcommon.js"></script>
<div style="width: 75px;">
<div id="target"></div>
</div>
<script>
// Simple tests
test_math_used('round(10,10)', '10', {type:'number'});
test_math_used('mod(1,1)', '0', {type:'number'});
test_math_used('rem(1,1)', '0', {type:'number'});
// Test basic round
test_math_used('calc(round(100,10))', '100', {type:'number'});
test_math_used('calc(round(up, 101,10))', '110', {type:'number'});
test_math_used('calc(round(down, 106,10))', '100', {type:'number'});
test_math_used('calc(round(to-zero, 105, 10))', '100', {type:'number'});
test_math_used('calc(round(to-zero, -105, 10))', '-100', {type:'number'});
test_math_used('calc(round(-100, 10))', '-100', {type:'number'});
test_math_used('calc(round(up, -103, 10))', '-100', {type:'number'});
// Test round when first number is a multiple of the second number.
for (let number of [0, 5, -5, 10, -10, 20, -20]) {
test_math_used(`round(up, ${number}, 5)`, `${number}`, {type:'number'});
test_math_used(`round(down, ${number}, 5)`, `${number}`, {type:'number'});
test_math_used(`round(nearest, ${number}, 5)`, `${number}`, {type:'number'});
test_math_used(`round(to-zero, ${number}, 5)`, `${number}`, {type:'number'});
}
// Test basic mod/rem
test_math_used('mod(18,5)', '3', {type:'number'});
test_math_used('rem(18,5)', '3', {type:'number'});
test_math_used('mod(-140,-90)', '-50', {type:'number'});
test_math_used('mod(-18,5)', '2', {type:'number'});
test_math_used('rem(-18,5)', '-3', {type:'number'});
test_math_used('mod(140,-90)', '-40', {type:'number'});
test_math_used('rem(140,-90)', '50', {type:'number'});
// Test basic calculations
test_math_used('calc(round(round(100,10), 10))', '100', {type:'number'});
test_math_used('calc(round(up, round(100,10) + 1,10))', '110', {type:'number'});
test_math_used('calc(round(down, round(100,10) + 2 * 3,10))', '100', {type:'number'});
test_math_used('calc(round(to-zero,round(100,10) * 2 - 95, 10))', '100', {type:'number'});
test_math_used('calc(round(round(100,10)* -1,10))', '-100', {type:'number'});
test_math_used('calc(round(up, -103 + -103 / -103 - 1,10))', '-100', {type:'number'});
test_math_used('calc(mod(18,5) * 2 + mod(17,5))', '8', {type:'number'});
test_math_used('calc(rem(mod(18,5),5))', '3', {type:'number'});
test_math_used('calc(rem(mod(18,5),mod(17,5)))', '1', {type:'number'});
test_math_used('calc(mod(-140,-90))', '-50', {type:'number'});
test_math_used('calc(mod(rem(1,18)* -1,5))', '4', {type:'number'});
// Type check
test_math_used('round(10px,6px)', '12px');
test_math_used('round(10cm,6cm)', '12cm');
test_math_used('round(10mm,6mm)', '12mm');
test_math_used('round(10Q, 6Q)', '12Q');
test_math_used('round(10in,6in)', '12in');
test_math_used('round(10pc,6pc)', '12pc');
test_math_used('round(10pt,6pt)', '12pt');
test_math_used('round(10em,6em)', '12em');
test_math_used('round(10ex,6ex)', '12ex');
test_math_used('round(10ch,6ch)', '12ch');
test_math_used('round(10rem,6rem)', '12rem');
test_math_used('round(10vh,6vh)', '12vh');
test_math_used('round(10vw,6vw)', '12vw');
test_math_used('round(10vmin,6vmin)', '12vmin');
test_math_used('round(10vmax,6vmax)', '12vmax');
test_math_used('round(10s,6s)', '12s', {type:'time'});
test_math_used('round(10ms,6ms)', '12ms', {type:'time'});
test_math_used('round(10deg,6deg)', '12deg', {type:'angle', approx:0.1});
test_math_used('round(10grad,6grad)', '12grad', {type:'angle', approx:0.1});
test_math_used('round(10rad,6rad)', '12rad',{type:'angle', approx:0.1});
test_math_used('round(10turn,6turn)', '12turn',{type:'angle', approx:0.1});
test_math_used('mod(10px,6px)', '4px');
test_math_used('mod(10cm,6cm)', '4cm');
test_math_used('mod(10mm,6mm)', '4mm');
test_math_used('mod(10Q, 6Q)', '4Q');
test_math_used('mod(10in,6in)', '4in');
test_math_used('mod(10pc,6pc)', '4pc');
test_math_used('mod(10em,6em)', '4em');
test_math_used('mod(10ex,6ex)', '4ex');
test_math_used('mod(10ch,6ch)', '4ch');
test_math_used('mod(10rem,6rem)', '4rem');
test_math_used('mod(10vh,6vh)', '4vh', {approx: 0.1});
test_math_used('mod(10vw,6vw)', '4vw', {approx: 0.1});
test_math_used('mod(10vmin,6vmin)', '4vmin', {approx: 0.1});
test_math_used('mod(10vmax,6vmax)', '4vmax', {approx: 0.1});
test_math_used('mod(10s,6s)', '4s', {type:'time'});
test_math_used('mod(10ms,6ms)', '4ms', {type:'time'});
test_math_used('mod(10deg,6deg)', '4deg', {type:'angle', approx:0.1});
test_math_used('mod(10grad,6grad)', '4grad', {type:'angle', approx:0.1});
test_math_used('mod(10rad,6rad)', '4rad',{type:'angle', approx:0.1});
test_math_used('mod(10turn,6turn)', '4turn',{type:'angle', approx:0.1});
test_math_used('rem(10px,6px)', '4px');
test_math_used('rem(10cm,6cm)', '4cm');
test_math_used('rem(10mm,6mm)', '4mm');
test_math_used('rem(10Q, 6Q)', '4Q');
test_math_used('rem(10in,6in)', '4in');
test_math_used('rem(10pc,6pc)', '4pc');
test_math_used('rem(10em,6em)', '4em');
test_math_used('rem(10ex,6ex)', '4ex');
test_math_used('rem(10ch,6ch)', '4ch');
test_math_used('rem(10rem,6rem)', '4rem');
test_math_used('rem(10vh,6vh)', '4vh', {approx: 0.1});
test_math_used('rem(10vw,6vw)', '4vw', {approx: 0.1});
test_math_used('rem(10vmin,6vmin)', '4vmin', {approx: 0.1});
test_math_used('rem(10vmax,6vmax)', '4vmax', {approx: 0.1});
test_math_used('rem(10s,6s)', '4s', {type:'time'});
test_math_used('rem(10ms,6ms)', '4ms', {type:'time'});
test_math_used('rem(10deg,6deg)', '4deg', {type:'angle', approx:0.1});
test_math_used('rem(10grad,6grad)', '4grad', {type:'angle', approx:0.1});
test_math_used('rem(10rad,6rad)', '4rad',{type:'angle', approx:0.1});
test_math_used('rem(10turn,6turn)', '4turn',{type:'angle', approx:0.1});
//Test percentage and mixed units
test_math_used('round(10%,1px)', '8px');
test_math_used('round(10%,5px)', '10px');
test_math_used('round(2rem,5px)', '30px');
test_math_used('round(100px,1rem)', '96px');
test_math_used('round(10s,6000ms)', '12s', {type:'time'});
test_math_used('round(10000ms,6s)', '12s', {type:'time'});
test_math_used('mod(10%,1px)', '0.5px');
test_math_used('mod(10%,5px)', '2.5px');
test_math_used('mod(2rem,5px)', '2px');
test_math_used('mod(100px,1rem)', '4px');
test_math_used('mod(10s,6000ms)', '4s', {type:'time'});
test_math_used('mod(10000ms,6s)', '4s', {type:'time'});
test_math_used('mod(18px,100% / 15)', '3px', {approx: 0.1});
test_math_used('mod(-18px,100% / 10)', '4.5px');
test_math_used('mod(18%,5%)', '3%');
test_math_used('mod(-19%,5%)', '1%');
test_math_used('mod(18vw,5vw)', '3vw');
test_math_used('mod(-18vw,5vw)', '2vw', {approx: 0.1});
test_math_used('rem(10%,1px)', '0.5px');
test_math_used('rem(10%,5px)', '2.5px');
test_math_used('rem(2rem,5px)', '2px');
test_math_used('rem(100px,1rem)', '4px');
test_math_used('rem(10s,6000ms)', '4s', {type:'time'});
test_math_used('rem(10000ms,6s)', '4s', {type:'time'});
test_math_used('rem(18px,100% / 15)', '3px', {approx: 0.1});
test_math_used('rem(-18px,100% / 15)', '-3px', {approx: 0.1});
test_math_used('rem(18vw,5vw)', '3vw');
test_math_used('rem(-18vw,5vw)', '-3vw');
test_math_used('calc(round(1px + 0%, 1px + 0%))', '1px');
test_math_used('calc(mod(3px + 0%, 2px + 0%))', '1px');
test_math_used('calc(rem(3px + 0%, 2px + 0%))', '1px');
test_math_used('round(1px + 0%, 1px)', '1px');
test_math_used('mod(3px + 0%, 2px)', '1px');
test_math_used('rem(3px + 0%, 2px)', '1px');
// In round(A, B), if B is 0, the result is NaN. If A and B are both infinite, the result is NaN.
// In mod(A, B) or rem(A, B), if B is 0, the result is NaN. If A is infinite, the result is NaN.
for (let operator of ['round', 'mod', 'rem']) {
test_math_used(`${operator}(0, 0)`, 'calc(NaN)', {type: 'number'});
test_math_used(`${operator}(-0, 0)`, 'calc(NaN)', {type: 'number'});
test_math_used(`${operator}(Infinity, 0)`, 'calc(NaN)', {type: 'number'});
test_math_used(`${operator}(-Infinity, 0)`, 'calc(NaN)', {type: 'number'});
test_math_used(`${operator}(-4, 0)`, 'calc(NaN)', {type: 'number'});
test_math_used(`${operator}(4, 0)`, 'calc(NaN)', {type: 'number'});
test_math_used(`${operator}(Infinity, Infinity)`, 'calc(NaN)', {type: 'number'});
test_math_used(`${operator}(-Infinity, -Infinity)`, 'calc(NaN)', {type: 'number'});
test_math_used(`${operator}(Infinity, -Infinity)`, 'calc(NaN)', {type: 'number'});
test_math_used(`${operator}(-Infinity, Infinity)`, 'calc(NaN)', {type: 'number'});
}
// In round(A, B), if A is infinite but B is finite, the result is the same infinity.
for (let roundingStrategy of ['up', 'down', 'nearest', 'to-zero']) {
test_math_used(`round(${roundingStrategy}, Infinity, 4)`, 'calc(Infinity)', {type: 'number'});
test_math_used(`round(${roundingStrategy}, -Infinity, 4)`, 'calc(-Infinity)', {type: 'number'});
test_math_used(`round(${roundingStrategy}, Infinity, -4)`, 'calc(Infinity)', {type: 'number'});
test_math_used(`round(${roundingStrategy}, -Infinity, -4)`, 'calc(-Infinity)', {type: 'number'});
}
// If A is finite but B is infinite, the result depends on the <rounding-strategy> and the sign of A:
// nearest & to-zero: If A is positive or 0⁺, return 0⁺. Otherwise, return 0⁻.
for (let roundingStrategy of ['nearest', 'to-zero']) {
test_math_used(`round(${roundingStrategy}, 0, Infinity)`, '0', {type: 'number'});
test_math_used(`round(${roundingStrategy}, 4, Infinity)`, '0', {type: 'number'});
test_math_used(`round(${roundingStrategy}, -0, Infinity)`, 'calc(-0)', {type: 'number'});
test_math_used(`round(${roundingStrategy}, -4, Infinity)`, 'calc(-0)', {type: 'number'});
test_math_used(`round(${roundingStrategy}, 0, -Infinity)`, '0', {type: 'number'});
test_math_used(`round(${roundingStrategy}, 4, -Infinity)`, '0', {type: 'number'});
test_math_used(`round(${roundingStrategy}, -0, -Infinity)`, 'calc(-0)', {type: 'number'});
test_math_used(`round(${roundingStrategy}, -4, -Infinity)`, 'calc(-0)', {type: 'number'});
}
// up: If A is positive (not zero), return +∞. If A is 0⁺, return 0⁺. Otherwise, return 0⁻.
test_math_used('round(up, 1, Infinity)', 'calc(Infinity)', {type: 'number'});
test_math_used('round(up, 0, Infinity)', '0', {type: 'number'});
test_math_used('round(up, -1, Infinity)', 'calc(-0)', {type: 'number'});
test_math_used('round(up, 1, -Infinity)', 'calc(Infinity)', {type: 'number'});
test_math_used('round(up, 0, -Infinity)', '0', {type: 'number'});
test_math_used('round(up, -1, -Infinity)', 'calc(-0)', {type: 'number'});
// down: If A is negative (not zero), return −∞. If A is 0⁻, return 0⁻. Otherwise, return 0⁺.
test_math_used('round(down, 1, Infinity)', 'calc(-0)', {type: 'number'});
test_math_used('round(down, 0, Infinity)', '0', {type: 'number'});
test_math_used('round(down, -1, Infinity)', 'calc(-Infinity)', {type: 'number'});
test_math_used('round(down, 1, -Infinity)', 'calc(-0)', {type: 'number'});
test_math_used('round(down, 0, -Infinity)', '0', {type: 'number'});
test_math_used('round(down, -1, -Infinity)', 'calc(-Infinity)', {type: 'number'});
// In mod(A, B) only, if B is infinite and A has opposite sign to B (including an oppositely-signed zero), the result is NaN.
test_math_used('mod(-0, Infinity)', 'calc(NaN)', {type: 'number'});
test_math_used('mod(0, -Infinity)', 'calc(NaN)', {type: 'number'});
test_math_used('mod(-4, Infinity)', 'calc(NaN)', {type: 'number'});
test_math_used('mod(4, -Infinity)', 'calc(NaN)', {type: 'number'});
</script>

View file

@ -0,0 +1,240 @@
<!DOCTYPE html>
<link rel="help" href="https://drafts.csswg.org/css-values-4/#comp-func">
<link rel="help" href="https://drafts.csswg.org/css-values-4/#numbers">
<link rel="help" href="https://drafts.csswg.org/css-values-4/#calc-type-checking">
<link rel="author" title="Apple Inc">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="../support/numeric-testcommon.js"></script>
<div id="container" style="font-size: 20px; width: 100px">
<div id="target"></div>
</div>
<script>
function test_zero(expression, { is_negative }) {
test_math_used(`calc(${expression})`, '0', {type:'integer'});
// to test zero sign, make it to negative infinity and clamp it between -1 and 1
test_math_used(`clamp(-1, calc( 1 / sign(${expression})), 1)`, (is_negative)? '-1' : '1', {type:'integer'});
}
function test_length_equals(value, expected, msgExtra) {
test_math_used(value, expected, {msgExtra, type: 'integer'});
}
// Identity tests
test_math_used('abs(1)', '1', {type:'integer'});
test_math_used('sign(1)', '1', {type:'integer'});
test_math_used('abs(-1)', '1', {type:'integer'});
test_math_used('sign(-1)', '-1', {type:'integer'});
// Nestings
test_math_used('abs(sign(1))', '1', {type:'integer'});
test_math_used('abs(sign(sign(1)))', '1', {type:'integer'});
test_math_used('sign(sign(sign(1) + sign(1)))', '1', {type:'integer'});
// General calculations
test_math_used('calc(abs(0.1 + 0.2) + 0.05)', '0.35', {type:'number', approx:0.1});
test_math_used('calc(sign(0.1 + 0.2) - 0.05)', '0.95', {type:'number', approx:0.1});
test_math_used('calc(abs(0.1 + 0.2) * 2)', '0.6', {type:'number', approx:0.1});
test_math_used('calc(abs(sign(0.1) + 0.2) / 2)', '0.6', {type:'number', approx:0.1});
test_math_used('calc(abs(0.1 + 0.2) * -2)', '-0.6', {type:'number', approx:0.1});
test_math_used('calc(sign(0.1 - 0.2) - 0.05)', '-1.05', {type:'number', approx:0.1});
test_math_used('calc(sign(1) + sign(1) - 0.05)', '1.95', {type:'number', approx:0.1});
// Test with <length-percentage>
test_math_used('abs(10px)', '10px', {type:'length'});
test_math_used('abs(10%)', '10px', {type:'length'});
test_math_used('abs(10px + 10%)', '20px', {type:'length'});
test_math_used('calc(10px + abs(10%))', '20px', {type:'length'});
test_math_used('abs(-10px)', '10px', {type:'length'});
test_math_used('abs(-10%)', '10px', {type:'length'});
// (20px + 1px) * (sign(20px - 10px - 10px) + 1)
test_math_used('calc((1em + 1px) * (sign(1em - 10px - 10%) + 1))', '21px', {type:'length'});
// Test sign for zero
test_zero('calc(sign(-0))', {is_negative: true});
test_zero('calc(sign(0))', {is_negative: false});
// Test with NaN and inf
test_math_used('abs(infinity)', 'calc(infinity)', {type:'number'});
test_math_used('abs(-infinity)', 'calc(infinity)', {type:'number'});
test_math_used('abs(NaN)', 'calc(NaN)', {type:'number'});
// Test abs/sign with negate
test_math_used('calc(20 - abs(-10))', '10', {type:'number'});
test_math_used('calc(20 - abs(10))', '10', {type:'number'});
test_math_used('calc(10 - abs(10 - abs(-30))', '-10', {type:'number'});
test_math_used('calc(2 - sign(1))', '1', {type:'number'});
test_math_used('calc(2 - sign(-1))', '3', {type:'number'});
test_math_used('calc(2 - sign(1 - sign(-1)))', '1', {type:'number'});
test_math_used('calc(10 - abs(20 - sign(2 - abs(-20))))', '-11', {type:'number'});
// Type checking sign
test_math_used('sign(1px)', '1', {type:'integer'});
test_math_used('sign(1cm)', '1', {type:'integer'});
test_math_used('sign(1mm)', '1', {type:'integer'});
test_math_used('sign(1Q)', '1', {type:'integer'});
test_math_used('sign(1in)', '1', {type:'integer'});
test_math_used('sign(1pc)', '1', {type:'integer'});
test_math_used('sign(1pt)', '1', {type:'integer'});
test_math_used('sign(1em)', '1', {type:'integer'});
test_math_used('sign(1ex)', '1', {type:'integer'});
test_math_used('sign(1ch)', '1', {type:'integer'});
test_math_used('sign(1rem)', '1', {type:'integer'});
test_math_used('sign(1vh)', '1', {type:'integer'});
test_math_used('sign(1vw)', '1', {type:'integer'});
test_math_used('sign(1vmin)', '1', {type:'integer'});
test_math_used('sign(1vmax)', '1', {type:'integer'});
test_math_used('sign(-1px)', '-1', {type:'integer'});
test_math_used('sign(-1cm)', '-1', {type:'integer'});
test_math_used('sign(-1mm)', '-1', {type:'integer'});
test_math_used('sign(-1Q)', '-1', {type:'integer'});
test_math_used('sign(-1in)', '-1', {type:'integer'});
test_math_used('sign(-1pc)', '-1', {type:'integer'});
test_math_used('sign(-1pt)', '-1', {type:'integer'});
test_math_used('sign(-1em)', '-1', {type:'integer'});
test_math_used('sign(-1ex)', '-1', {type:'integer'});
test_math_used('sign(-1ch)', '-1', {type:'integer'});
test_math_used('sign(-1rem)', '-1', {type:'integer'});
test_math_used('sign(-1vh)', '-1', {type:'integer'});
test_math_used('sign(-1vw)', '-1', {type:'integer'});
test_math_used('sign(-1vmin)', '-1', {type:'integer'});
test_math_used('sign(-1vmax)', '-1', {type:'integer'});
test_math_used('sign(1s)', '1', {type:'integer'});
test_math_used('sign(1ms)', '1', {type:'integer'});
test_math_used('sign(-1s)', '-1', {type:'integer'});
test_math_used('sign(-1ms)', '-1', {type:'integer'});
test_math_used('sign(1deg)', '1', {type:'integer'});
test_math_used('sign(1grad)', '1', {type:'integer'});
test_math_used('sign(1rad)', '1', {type:'integer'});
test_math_used('sign(1turn)', '1', {type:'integer'});
test_math_used('sign(-1deg)', '-1', {type:'integer'});
test_math_used('sign(-1grad)', '-1', {type:'integer'});
test_math_used('sign(-1rad)', '-1', {type:'integer'});
test_math_used('sign(-1turn)', '-1', {type:'integer'});
test_zero('sign(0px)', {is_negative: false});
test_zero('sign(0cm)', {is_negative: false});
test_zero('sign(0mm)', {is_negative: false});
test_zero('sign(0Q)', {is_negative: false});
test_zero('sign(0in)', {is_negative: false});
test_zero('sign(0pc)', {is_negative: false});
test_zero('sign(0pt)', {is_negative: false});
test_zero('sign(0em)', {is_negative: false});
test_zero('sign(0ex)', {is_negative: false});
test_zero('sign(0ch)', {is_negative: false});
test_zero('sign(0rem)', {is_negative: false});
test_zero('sign(0vh)', {is_negative: false});
test_zero('sign(0vw)', {is_negative: false});
test_zero('sign(0vmin)', {is_negative: false});
test_zero('sign(0vmax)', {is_negative: false});
test_zero('sign(-0px)', {is_negative: true});
test_zero('sign(-0cm)', {is_negative: true});
test_zero('sign(-0mm)', {is_negative: true});
test_zero('sign(-0Q)', {is_negative: true});
test_zero('sign(-0in)', {is_negative: true});
test_zero('sign(-0pc)', {is_negative: true});
test_zero('sign(-0pt)', {is_negative: true});
test_zero('sign(-0em)', {is_negative: true});
test_zero('sign(-0ex)', {is_negative: true});
test_zero('sign(-0ch)', {is_negative: true});
test_zero('sign(-0rem)', {is_negative: true});
test_zero('sign(-0vh)', {is_negative: true});
test_zero('sign(-0vw)', {is_negative: true});
test_zero('sign(-0vmin)', {is_negative: true});
test_zero('sign(-0vmax)', {is_negative: true});
test_zero('sign(0s)', {is_negative: false});
test_zero('sign(0ms)', {is_negative: false});
test_zero('sign(-0s)', {is_negative: true});
test_zero('sign(-0ms)', {is_negative: true});
test_zero('sign(0deg)', {is_negative: false});
test_zero('sign(0grad)', {is_negative: false});
test_zero('sign(0rad)', {is_negative: false});
test_zero('sign(0turn)', {is_negative: false});
test_zero('sign(-0deg)', {is_negative: true});
test_zero('sign(-0grad)', {is_negative: true});
test_zero('sign(-0rad)', {is_negative: true});
test_zero('sign(-0turn)', {is_negative: true});
// Type checking abs
test_math_used('abs(1px)', '1px');
test_math_used('abs(1cm)', '1cm');
test_math_used('abs(1mm)', '1mm');
test_math_used('abs(1Q)', '1Q');
test_math_used('abs(1in)', '1in');
test_math_used('abs(1pc)', '1pc');
test_math_used('abs(1pt)', '1pt');
test_math_used('abs(1em)', '1em');
test_math_used('abs(1ex)', '1ex');
test_math_used('abs(1ch)', '1ch');
test_math_used('abs(1rem)', '1rem');
test_math_used('abs(1vh)', '1vh');
test_math_used('abs(1vw)', '1vw');
test_math_used('abs(1vmin)', '1vmin');
test_math_used('abs(1vmax)', '1vmax');
test_math_used('abs(-1px)', '1px');
test_math_used('abs(-1cm)', '1cm');
test_math_used('abs(-1mm)', '1mm');
test_math_used('abs(-1Q)', '1Q');
test_math_used('abs(-1in)', '1in');
test_math_used('abs(-1pc)', '1pc');
test_math_used('abs(-1pt)', '1pt');
test_math_used('abs(-1em)', '1em');
test_math_used('abs(-1ex)', '1ex');
test_math_used('abs(-1ch)', '1ch');
test_math_used('abs(-1rem)', '1rem');
test_math_used('abs(-1vh)', '1vh');
test_math_used('abs(-1vw)', '1vw');
test_math_used('abs(-1vmin)', '1vmin');
test_math_used('abs(-1vmax)', '1vmax');
test_math_used('abs(1s)', '1s', {type:'time'});
test_math_used('abs(1ms)', '1ms', {type:'time'});
test_math_used('abs(-1s)', '1s', {type:'time'});
test_math_used('abs(-1ms)', '1ms', {type:'time'});
test_math_used('abs(1deg)', '1deg', {type:'angle', approx:0.001});
test_math_used('abs(1grad)', '1grad', {type:'angle', approx:0.001});
test_math_used('abs(1rad)', '1rad', {type:'angle', approx:0.001});
test_math_used('abs(1turn)', '1turn', {type:'angle', approx:0.001});
test_math_used('abs(-1deg)', '1deg', {type:'angle', approx:0.001});
test_math_used('abs(-1grad)', '1grad', {type:'angle', approx:0.001});
test_math_used('abs(-1rad)', '1rad', {type:'angle', approx:0.001});
test_math_used('abs(-1turn)', '1turn', {type:'angle', approx:0.001});
// with relative length
document.getElementById('container').style.fontSize = '10px';
test_length_equals('sign(10px - 1em)', '0', 'fontSize=10px');
test_length_equals('sign(10px - 2em)', '-1', 'fontSize=10px');
document.getElementById('container').style.fontSize = '20px';
test_math_used('calc(sign(10%) * 100px)', '100px');
// Basic tests (just px and em) for sign() interaction with units not
// otherwise used by the property.
test_math_used('calc(2.5 - sign(41px - 2em) / 2)', '2', {type:'number'});
test_math_used('calc(2.5 - sign(40px - 2em) / 2)', '2.5', {type:'number'});
test_math_used('calc(2.5 - sign(39px - 2em) / 2)', '3', {type:'number'});
test_math_used('calc(3 + sign(42px - 2em))', '4', {type:'integer'});
test_math_used('calc(3 + sign(40px - 2em))', '3', {type:'integer'});
test_math_used('calc(3 + sign(38px - 2em))', '2', {type:'integer'});
test_math_used('calc(50px + 100px * sign(42px - 2em))', '150px', {type:'length'});
test_math_used('calc(50px + 100px * sign(40px - 2em))', '50px', {type:'length'});
test_math_used('calc(50px + 100px * sign(38px - 2em))', '-50px', {type:'length'});
test_math_used('calc(90deg + 30deg * sign(42px - 2em))', '120deg', {type:'angle'});
test_math_used('calc(90deg + 30deg * sign(40px - 2em))', '90deg', {type:'angle'});
test_math_used('calc(90deg + 30deg * sign(38px - 2em))', '60deg', {type:'angle'});
test_math_used('calc(5s + 15s * sign(42px - 2em))', '20s', {type:'time'});
test_math_used('calc(5s + 15s * sign(40px - 2em))', '5s', {type:'time'});
test_math_used('calc(5s + 15s * sign(38px - 2em))', '-10s', {type:'time'});
test_math_used('calc(100dpi + 20dpi * sign(42px - 2em))', '120dpi', {type:'resolution'});
test_math_used('calc(100dpi + 20dpi * sign(40px - 2em))', '100dpi', {type:'resolution'});
test_math_used('calc(100dpi + 20dpi * sign(38px - 2em))', '80dpi', {type:'resolution'});
test_math_used('calc(3fr + 1fr * sign(42px - 2em))', '4fr', {type:'flex'});
test_math_used('calc(3fr + 1fr * sign(40px - 2em))', '3fr', {type:'flex'});
test_math_used('calc(3fr + 1fr * sign(38px - 2em))', '2fr', {type:'flex'});
// repeat a few with px and rem
test_math_used('calc(2.5 - sign(33px - 2rem) / 2)', '2', {type:'number'});
test_math_used('calc(2.5 - sign(32px - 2rem) / 2)', '2.5', {type:'number'});
test_math_used('calc(2.5 - sign(31px - 2rem) / 2)', '3', {type:'number'});
test_math_used('calc(50px + 100px * sign(34px - 2rem))', '150px', {type:'length'});
test_math_used('calc(50px + 100px * sign(32px - 2rem))', '50px', {type:'length'});
test_math_used('calc(50px + 100px * sign(30px - 2rem))', '-50px', {type:'length'});
</script>

View file

@ -0,0 +1,198 @@
'use strict';
/*
Provides functions to help test that two numeric values are equivalent.
These *do not* rely on you predicting what one value will serialize to;
instead, they set and serialize *both* values,
and just ensure that they serialize to the same thing.
They rely on a #target element existing in the document,
as this might rely on layout to resolve styles,
and so it needs to be in the document.
Three main functions are defined, with the same signatures:
test_math_used() (for testing used values),
test_math_computed() (for testing computed values),
and test_math_specified() (for testing specified values).
Signature for all is:
test_math_X(
testString, // A numeric value; required.
expectedString, // A hopefully-equivalent numeric value; required.
{ // all of these are optional
type, // "number", "length", etc. See impl for full list. Defaults to "length".
msg, // The message to display for the test; autogenned if not provided.
msgExtra, // Extra info to put after the auto-genned message.
prop, // If you want to override the automatic choice of tested property.
extraStyle, // Styles that need to be set at the same time to properly test the value.
approx, // The epsilon in order to compare numeric-ish values.
// Note that it'd use parseFloat in order to extract the
// values, so they can drop units or what not.
}
);
Additionally, five specialized functions are provided
to test that a given value is ±, ±0, or NaN:
* test_plus_infinity(testString)
* test_minus_infinity(testString)
* test_plus_zero(testString)
* test_minus_zero(testString)
* test_nan(testString)
*/
function test_math_used(testString, expectedString, {approx, msg, msgExtra, type, prop, extraStyle={}}={}) {
if(type === undefined) type = "length";
if(!prop) {
switch(type) {
case "number": prop = "scale"; break;
case "integer": prop = "z-index"; extraStyle.position="absolute"; break;
case "length": prop = "margin-left"; break;
case "angle": prop = "rotate"; break;
case "time": prop = "transition-delay"; break;
case "resolution": prop = "image-resolution"; break;
case "flex": prop = "grid-template-rows"; break;
default: throw Exception(`Value type '${type}' isn't capable of math.`);
}
}
_test_math({stage:'used', testString, expectedString, type, approx, msg, msgExtra, prop, extraStyle});
}
function test_math_computed(testString, expectedString, {approx, msg, msgExtra, type, prop, extraStyle={}}={}) {
if(type === undefined) type = "length";
if(!prop) {
switch(type) {
case "number": prop = "scale"; break;
case "integer": prop = "z-index"; extraStyle.position="absolute"; break;
case "length": prop = "flex-basis"; break;
case "angle": prop = "rotate"; break;
case "time": prop = "transition-delay"; break;
case "resolution": prop = "image-resolution"; break;
case "flex": prop = "grid-template-rows"; break;
default: throw Exception(`Value type '${type}' isn't capable of math.`);
}
}
_test_math({stage:'computed', testString, expectedString, type, approx, msg, msgExtra, prop, extraStyle});
}
function test_math_specified(testString, expectedString, {approx, msg, msgExtra, type, prop, extraStyle={}}={}) {
if(type === undefined) type = "length";
const stage = "specified";
if(!prop) {
switch(type) {
case "number": prop = "scale"; break;
case "integer": prop = "z-index"; extraStyle.position="absolute"; break;
case "length": prop = "flex-basis"; break;
case "angle": prop = "rotate"; break;
case "time": prop = "transition-delay"; break;
case "resolution": prop = "image-resolution"; break;
case "flex": prop = "grid-template-rows"; break;
default: throw Exception(`Value type '${type}' isn't capable of math.`);
}
}
// Find the test element
const testEl = document.getElementById('target');
if(testEl == null) throw "Couldn't find #target element to run tests on.";
// Then reset its styles
testEl.style = "";
for(const p in extraStyle) {
testEl.style[p] = extraStyle[p];
}
if(!msg) {
msg = `${testString} should be ${stage}-value-equivalent to ${expectedString}`;
if(msgExtra) msg += "; " + msgExtra;
}
let t = testString;
let e = expectedString;
test(()=>{
testEl.style[prop] = '';
testEl.style[prop] = t;
const usedValue = testEl.style[prop];
assert_not_equals(usedValue, '', `${testString} isn't valid in '${prop}'; got the default value instead.`);
testEl.style[prop] = '';
testEl.style[prop] = e;
const expectedValue = testEl.style[prop];
assert_not_equals(expectedValue, '', `${expectedString} isn't valid in '${prop}'; got the default value instead.`)
if (approx) {
let extractValue = function(value) {
if (value.startsWith("calc(")) {
value = value.slice("calc(".length, -1);
}
return parseFloat(value);
};
let parsedUsed = extractValue(usedValue);
let parsedExpected = extractValue(expectedValue);
assert_approx_equals(parsedUsed, parsedExpected, approx, `${testString} and ${expectedString} ${approx} serialize to the same thing in ${stage} values.`);
} else {
assert_equals(usedValue, expectedValue, `${testString} and ${expectedString} serialize to the same thing in ${stage} values.`);
}
}, msg);
}
/*
All of these expect the testString to evaluate to a <number>.
*/
function test_plus_infinity(testString) {
test_math_used(testString, "calc(infinity)", {type:"number"});
}
function test_minus_infinity(testString) {
test_math_used(testString, "calc(-infinity)", {type:"number"});
}
function test_plus_zero(testString) {
test_math_used(`calc(1 / ${testString})`, "calc(infinity)", {type:"number"});
}
function test_minus_zero(testString) {
test_math_used(`calc(1 / ${testString})`, "calc(-infinity)", {type:"number"});
}
function test_nan(testString) {
// Make sure that it's NaN, not an infinity,
// by making sure that it's the same value both pos and neg.
test_math_used(testString, "calc(NaN)", {type:"number"});
test_math_used(`calc(-1 * ${testString})`, "calc(NaN)", {type:"number"});
}
function _test_math({stage, testEl, testString, expectedString, type, approx, msg, msgExtra, prop, extraStyle}={}) {
// Find the test element
if(!testEl) testEl = document.getElementById('target');
if(testEl == null) throw "Couldn't find #target element to run tests on.";
// Then reset its styles
testEl.style = "";
for(const p in extraStyle) {
testEl.style[p] = extraStyle[p];
}
if(!msg) {
msg = `${testString} should be ${stage}-value-equivalent to ${expectedString}`;
if(msgExtra) msg += "; " + msgExtra;
}
let t = testString;
let e = expectedString;
test(()=>{
testEl.style[prop] = '';
const defaultValue = getComputedStyle(testEl)[prop];
testEl.style[prop] = t;
const usedValue = getComputedStyle(testEl)[prop];
assert_not_equals(usedValue, defaultValue, `${testString} isn't valid in '${prop}'; got the default value instead.`);
testEl.style[prop] = '';
testEl.style[prop] = e;
const expectedValue = getComputedStyle(testEl)[prop];
assert_not_equals(expectedValue, defaultValue, `${expectedString} isn't valid in '${prop}'; got the default value instead.`)
if (approx) {
let extractValue = function(value) {
return parseFloat(value);
};
let parsedUsed = extractValue(usedValue);
let parsedExpected = extractValue(expectedValue);
assert_approx_equals(parsedUsed, parsedExpected, approx, `${testString} and ${expectedString} ${approx} serialize to the same thing in ${stage} values.`);
} else {
assert_equals(usedValue, expectedValue, `${testString} and ${expectedString} serialize to the same thing in ${stage} values.`);
}
}, msg);
}