<!DOCTYPE html>
<meta charset="utf8">
<title>Test that custom property cycles behave correctly</title>
<link rel="help" href="https://drafts.csswg.org/css-variables/#cycles">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<main id=main></main>
<script>

  // Test that, for the given list of |declarations|, the computed values
  // of properties listed in |expected_invalid| are invalid (i.e. empty string),
  // and the computed values listed in |expected_valid| are *not* invalid
  // (i.e. not the empty string).
  function test_cycles(declarations, expected_invalid, expected_valid, description) {
    test(() => {
      let element = document.createElement('div');

      try {
        declarations.push('--sanity:valid');
        element.style = declarations.join(';');
        main.append(element);
        let cs = getComputedStyle(element);

        for (let e of expected_invalid)
          assert_equals(cs.getPropertyValue(e), '', `${e}`);
        for (let e of expected_valid)
          assert_not_equals(cs.getPropertyValue(e), '', `${e}`);

        assert_equals(cs.getPropertyValue('--sanity'), 'valid', '--sanity');

      } finally {
        element.remove();
      }
    }, description);
  }

  // (Diagrams produced with graph-easy).

  //  ┌───┐
  //  │   │ ───┐
  //  │ a │    │
  //  │   │ ◀──┘
  //  └───┘
  test_cycles(
    ['--a:var(--a)'],
    ['--a'],
    [],
    'Self-cycle');


  // ┌───┐
  // │ a │ ◀┐
  // └───┘  │
  //   │    │
  //   │    │
  //   ▼    │
  // ┌───┐  │
  // │ b │ ─┘
  // └───┘
  test_cycles(
    [
      '--a:var(--b)',
      '--b:var(--a)',
    ],
    ['--a', '--b'],
    [],
    'Simple a/b cycle');


  //  ┌───┐
  //  │ a │ ◀┐
  //  └───┘  │
  //    │    │
  //    │    │
  //    ▼    │
  //  ┌───┐  │
  //  │ b │  │
  //  └───┘  │
  //    │    │
  //    │    │
  //    ▼    │
  //  ┌───┐  │
  //  │ c │ ─┘
  //  └───┘
  test_cycles(
    [
      '--a:var(--b, cycle)',
      '--b:var(--c, cycle)',
      '--c:var(--a, cycle)',
    ],
    ['--a', '--b', '--c'],
    [],
    'Three-var cycle');


  //  ┌───┐
  //  │ x │
  //  └───┘
  //    │
  //    │
  //    ▼
  //  ┌───┐
  //  │ y │
  //  └───┘
  //    │
  //    │
  //    ▼
  //  ┌───┐
  //  │ a │ ◀┐
  //  └───┘  │
  //    │    │
  //    │    │
  //    ▼    │
  //  ┌───┐  │
  //  │ b │  │
  //  └───┘  │
  //    │    │
  //    │    │
  //    ▼    │
  //  ┌───┐  │
  //  │ c │ ─┘
  //  └───┘
  test_cycles(
    [
      '--x:var(--y, valid)',
      '--y:var(--a, valid)',
      '--a:var(--b, cycle)',
      '--b:var(--c, cycle)',
      '--c:var(--a, cycle)',
    ],
    ['--a', '--b', '--c'],
    ['--x', '--y'],
    'Cycle that starts in the middle of a chain');


  //  ┌───┐
  //  │ x │
  //  └───┘
  //    │
  //    │
  //    ▼
  //  ┌───┐
  //  │ a │ ◀┐
  //  └───┘  │
  //    │    │
  //    │    │
  //    ▼    │
  //  ┌───┐  │
  //  │ b │  │
  //  └───┘  │
  //    │    │
  //    │    │
  //    ▼    │
  //  ┌───┐  │
  //  │ c │ ─┘
  //  └───┘
  //    │
  //    │
  //    ▼
  //  ┌───┐
  //  │ y │
  //  └───┘
  test_cycles(
    [
      '--x:var(--a, valid)',
      '--a:var(--b, cycle)',
      '--b:var(--c, cycle)',
      '--c:var(--a, cycle) var(--y)',
      '--y:valid'
    ],
    ['--a', '--b', '--c'],
    ['--x', '--y'],
    'Cycle with extra edge');


  //            ┌───┐
  //            │ x │
  //            └───┘
  //              │
  //              │
  //              ▼
  //            ┌───┐
  //            │ a │ ◀┐
  //            └───┘  │
  //              │    │
  //              │    │
  //              ▼    │
  //  ┌───┐     ┌───┐  │
  //  │ y │ ◀── │ b │  │
  //  └───┘     └───┘  │
  //              │    │
  //              │    │
  //              ▼    │
  //            ┌───┐  │
  //            │ c │ ─┘
  //            └───┘
  test_cycles(
    [
      '--x:var(--a, valid)',
      '--a:var(--b, cycle)',
      '--b:var(--c, cycle) var(--y)',
      '--c:var(--a, cycle)',
      '--y:valid'
    ],
    ['--a', '--b', '--c'],
    ['--x', '--y'],
    'Cycle with extra edge (2)');


  //  ┌───┐
  //  │ x │
  //  └───┘
  //    │
  //    │
  //    ▼
  //  ┌───┐
  //  │ a │ ◀┐
  //  └───┘  │
  //    │    │
  //    │    │
  //    ▼    │
  //  ┌───┐  │
  //  │ b │  │
  //  └───┘  │
  //    │    │
  //    │    │
  //    ▼    │
  //  ┌───┐  │
  //  │ c │ ─┘
  //  └───┘
  //    │
  //    │
  //    ▼
  //  ┌───┐
  //  │ y │
  //  └───┘
  //    │
  //    │
  //    ▼
  //  ┌───┐
  //  │ z │
  //  └───┘
  test_cycles(
    [
      '--x:var(--a, valid)',
      '--a:var(--b, cycle)',
      '--b:var(--c, cycle)',
      '--c:var(--a, cycle) var(--y)',
      '--y:var(--z)',
      '--z:valid'
    ],
    ['--a', '--b', '--c'],
    ['--x', '--y', '--z'],
    'Cycle with extra edge (3)');

  //     ┌───┐
  //     │ x │
  //     └───┘
  //       │
  //       │
  //       ▼
  //     ┌───┐
  //     │ a │ ◀┐
  //     └───┘  │
  //       │    │
  //       │    │
  //       ▼    │
  //     ┌───┐  │
  //  ┌▶ │ b │ ─┘
  //  │  └───┘
  //  │    │
  //  │    │
  //  │    ▼
  //  │  ┌───┐
  //  │  │ c │
  //  │  └───┘
  //  │    │
  //  │    │
  //  │    ▼
  //  │  ┌───┐
  //  └─ │ d │
  //     └───┘
  test_cycles(
    [
      '--x:var(--a, valid)',
      '--a:var(--b, cycle)',
      '--b:var(--c, cycle) var(--a, cycle)',
      '--c:var(--d, cycle)',
      '--d:var(--b, cycle)',
    ],
    ['--a', '--b', '--c', '--d'],
    ['--x'],
    'Cycle with secondary cycle');


  //       ┌───┐
  //       │ x │
  //       └───┘
  //         │
  //         │
  //         ▼
  //       ┌───┐
  //       │ a │ ◀┐
  //       └───┘  │
  //         │    │
  //         │    │
  //         ▼    │
  //       ┌───┐  │
  //    ┌▶ │ b │  │
  //    │  └───┘  │
  //    │    │    │
  //    │    │    │
  //    │    ▼    │
  //    │  ┌───┐  │
  //    │  │ c │ ─┘
  //    │  └───┘
  //    │    │
  //    │    │
  //    │    ▼
  //    │  ┌───┐
  //    └─ │ d │
  //       └───┘
  //         │
  //         │
  //         ▼
  //       ┌───┐
  //       │ y │
  //       └───┘
  test_cycles(
    [
      '--x:var(--a, valid)',
      '--a:var(--b, cycle)',
      '--b:var(--c, cycle)',
      '--c:var(--d, cycle) var(--a, cycle)',
      '--d:var(--b, cycle) var(--y)',
      '--y:valid'
    ],
    ['--a', '--b', '--c', '--d'],
    ['--x', '--y'],
    'Cycle with overlapping secondary cycle');


  //    ┌──────────────┐
  //    │              │
  //    │       ┌───┐  │
  //    │       │ x │  │
  //    │       └───┘  │
  //    │         │    │
  //    │         │    │
  //    │         ▼    ▼
  //  ┌───┐     ┌────────┐     ┌───┐
  //  │ b │ ◀── │   a    │ ──▶ │ y │
  //  └───┘     └────────┘     └───┘
  //              │    ▲
  //              │    │
  //              ▼    │
  //            ┌───┐  │
  //            │ c │  │
  //            └───┘  │
  //              │    │
  //              │    │
  //              ▼    │
  //            ┌───┐  │
  //            │ d │ ─┘
  //            └───┘
  test_cycles(
    [
      '--x:var(--a, valid)',
      '--a:var(--b, cycle) var(--y, valid) var(--c, cycle)',
      '--b:var(--a, cycle) ',
      '--c:var(--d, cycle)',
      '--d:var(--a, cycle)',
      '--y:valid',
    ],
    ['--a', '--b', '--c', '--d'],
    ['--x', '--y'],
    'Cycle with deeper secondary cycle');


  //            ┌───┐
  //            │ x │
  //            └───┘
  //              │
  //              │
  //              ▼
  //            ┌───┐
  //    ┌─────▶ │ a │ ─┐
  //    │       └───┘  │
  //    │         │    │
  //    │         │    │
  //    │         ▼    │
  //    │       ┌───┐  │
  //    │    ┌─ │ b │  │
  //    │    │  └───┘  │
  //    │    │    │    │
  //    │    │    │    │
  //    │    │    ▼    │
  //    │    │  ┌───┐  │
  //    └────┼─ │ c │  │
  //         │  └───┘  │
  //         │    │    │
  //         │    │    │
  //         │    ▼    │
  //         │  ┌───┐  │
  //         └▶ │ y │ ◀┘
  //            └───┘
  test_cycles(
    [
      '--x:var(--a, valid)',
      '--a:var(--y, var(--b, cycle))',
      '--b:var(--y, var(--c, cycle))',
      '--c:var(--y, var(--a, cycle))',
      '--y:valid'
    ],
    ['--a', '--b', '--c'],
    ['--x', '--y'],
    'Cycle via fallback');

</script>
