Skip to content

[Test] Add unit tests for srt/constrained module#21010

Merged
ispobock merged 24 commits intosgl-project:mainfrom
vaibhawvipul:unit-tests-constrained
Mar 23, 2026
Merged

[Test] Add unit tests for srt/constrained module#21010
ispobock merged 24 commits intosgl-project:mainfrom
vaibhawvipul:unit-tests-constrained

Conversation

@vaibhawvipul
Copy link
Copy Markdown
Contributor

@vaibhawvipul vaibhawvipul commented Mar 20, 2026

Motivation

Addresses #20865 — the srt/constrained module currently has no unit test coverage. No server launch.

Coverage

Name                                                        Stmts   Miss  Cover   Missing
-----------------------------------------------------------------------------------------
python/sglang/srt/constrained/base_grammar_backend.py         125      4    97%   80, 84, 88, 127
python/sglang/srt/constrained/grammar_manager.py              113      0   100%
python/sglang/srt/constrained/llguidance_backend.py           107     73    32%   43-55, 58-64, 67-74, 77, 80-81, 86-95, 99, 103, 106, 112-116, 119, 124, 127-128, 140-145, 148-155, 158-169, 172-173, 176-181, 184-200
python/sglang/srt/constrained/outlines_backend.py             105     69    34%   48-51, 54, 59, 63, 66-71, 75, 78, 81-102, 105-106, 111, 120-143, 146-158, 161, 164, 167-175, 178, 184-190
python/sglang/srt/constrained/outlines_jump_forward.py        118     87    26%   34-36, 56, 63-139, 144, 147-157, 160-172, 175, 182-188
python/sglang/srt/constrained/reasoner_grammar_backend.py      69      0   100%
python/sglang/srt/constrained/triton_ops/bitmask_ops.py        47     39    17%   60-77, 89-129
python/sglang/srt/constrained/utils.py                          7      0   100%
python/sglang/srt/constrained/xgrammar_backend.py             176    132    25%   42, 64-71, 74-85, 88-89, 92, 97, 100, 104, 107-117, 120-129, 139-142, 145-146, 151-163, 166, 183-210, 215-232, 236-238, 243-248, 258-270, 273-278, 281-286, 289-315, 320, 324-352
-----------------------------------------------------------------------------------------
TOTAL                                                         867    400    54%

Test Plan

# command
python -m pytest test/registered/unit/constrained/test_utils.py -v
# output
=================================================================================================== test session starts ===================================================================================================
platform linux -- Python 3.12.3, pytest-9.0.2, pluggy-1.6.0 -- /root/sglang/.venv/bin/python
cachedir: .pytest_cache
rootdir: /root/sglang/test
configfile: pytest.ini
plugins: cov-7.0.0, anyio-4.12.1
collected 9 items                                                                                                                                                                                                         

test/registered/unit/constrained/test_utils.py::TestIsLegacyStructuralTag::test_both_keys_present_legacy_wins PASSED                                                                                                [ 11%]
test/registered/unit/constrained/test_utils.py::TestIsLegacyStructuralTag::test_empty_dict_raises PASSED                                                                                                            [ 22%]
test/registered/unit/constrained/test_utils.py::TestIsLegacyStructuralTag::test_legacy_format_empty_lists PASSED                                                                                                    [ 33%]
test/registered/unit/constrained/test_utils.py::TestIsLegacyStructuralTag::test_legacy_format_returns_true PASSED                                                                                                   [ 44%]
test/registered/unit/constrained/test_utils.py::TestIsLegacyStructuralTag::test_legacy_missing_triggers_raises PASSED                                                                                               [ 55%]
test/registered/unit/constrained/test_utils.py::TestIsLegacyStructuralTag::test_new_format_empty_format PASSED                                                                                                      [ 66%]
test/registered/unit/constrained/test_utils.py::TestIsLegacyStructuralTag::test_new_format_missing_format_raises PASSED                                                                                             [ 77%]
test/registered/unit/constrained/test_utils.py::TestIsLegacyStructuralTag::test_new_format_returns_false PASSED                                                                                                     [ 88%]
test/registered/unit/constrained/test_utils.py::TestIsLegacyStructuralTag::test_structures_none_uses_new_format_path PASSED                                                                                         [100%]

==================================================================================================== warnings summary =====================================================================================================
.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1428
  /root/sglang/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1428: PytestConfigWarning: Unknown config option: asyncio_mode
  
    self._warn_or_fail_if_strict(f"Unknown config option: {key}\n")

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
============================================================================================== 9 passed, 1 warning in 0.44s ===============================================================================================
# command 
python -m pytest test/registered/unit/constrained/test_base_grammar_backend.py -v
# output
platform linux -- Python 3.12.3, pytest-9.0.2, pluggy-1.6.0 -- /root/sglang/.venv/bin/python
cachedir: .pytest_cache
rootdir: /root/sglang/test
configfile: pytest.ini
plugins: cov-7.0.0, anyio-4.12.1
collected 54 items                                                                                                                                                                                                        

test/registered/unit/constrained/test_base_grammar_backend.py::TestGrammarStats::test_custom_values PASSED                                                                                                          [  1%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestGrammarStats::test_defaults PASSED                                                                                                               [  3%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestGrammarStats::test_tree_traversal_time_mutable_default PASSED                                                                                    [  5%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarObject::test_abstract_methods_raise PASSED                                                                                            [  7%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarObject::test_copy_returns_self PASSED                                                                                                 [  9%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarObject::test_finished_property PASSED                                                                                                 [ 11%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarObject::test_initial_state PASSED                                                                                                     [ 12%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarObject::test_is_terminated_default PASSED                                                                                             [ 14%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarObject::test_maybe_init_reasoning_noop PASSED                                                                                         [ 16%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarObject::test_static_methods_raise PASSED                                                                                              [ 18%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestInvalidGrammarObject::test_custom_error_message PASSED                                                                                           [ 20%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestInvalidGrammarObject::test_default_error_message PASSED                                                                                          [ 22%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestInvalidGrammarObject::test_inherits_finished_property PASSED                                                                                     [ 24%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestInvalidGrammarObject::test_is_base_grammar_object PASSED                                                                                         [ 25%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestInvalidGrammarObject::test_repr PASSED                                                                                                           [ 27%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarBackend::test_cache_hit_inits_reasoning PASSED                                                                                        [ 29%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarBackend::test_cache_hit_returns_copy PASSED                                                                                           [ 31%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarBackend::test_cache_miss_duplicate_key_submits_separate_futures PASSED                                                                [ 33%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarBackend::test_cache_miss_returns_future PASSED                                                                                        [ 35%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarBackend::test_cache_overwrite_replaces_value PASSED                                                                                   [ 37%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarBackend::test_dispatch_ebnf_unsupported PASSED                                                                                        [ 38%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarBackend::test_dispatch_fallback_error_message_content PASSED                                                                          [ 40%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarBackend::test_dispatch_fallback_raises PASSED                                                                                         [ 42%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarBackend::test_dispatch_json_unsupported PASSED                                                                                        [ 44%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarBackend::test_dispatch_regex_unsupported PASSED                                                                                       [ 46%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarBackend::test_dispatch_structural_tag_unsupported PASSED                                                                              [ 48%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarBackend::test_init_value_dispatch_no_stats PASSED                                                                                     [ 50%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarBackend::test_init_value_dispatch_none_grammar PASSED                                                                                 [ 51%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarBackend::test_init_value_dispatch_routes_ebnf PASSED                                                                                  [ 53%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarBackend::test_init_value_dispatch_routes_json PASSED                                                                                  [ 55%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarBackend::test_init_value_dispatch_routes_regex PASSED                                                                                 [ 57%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarBackend::test_init_value_dispatch_routes_structural_tag PASSED                                                                        [ 59%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarBackend::test_init_value_dispatch_sets_compilation_time PASSED                                                                        [ 61%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarBackend::test_init_value_dispatch_unknown_type_raises PASSED                                                                          [ 62%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarBackend::test_initial_cache_empty PASSED                                                                                              [ 64%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarBackend::test_multiple_cache_keys PASSED                                                                                              [ 66%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarBackend::test_reset_clears_cache PASSED                                                                                               [ 68%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarBackend::test_reset_then_miss PASSED                                                                                                  [ 70%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestBaseGrammarBackend::test_set_and_get_cache PASSED                                                                                                [ 72%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestRegisterGrammarBackend::test_overwrite_registration PASSED                                                                                       [ 74%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestRegisterGrammarBackend::test_register_and_use PASSED                                                                                             [ 75%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestCreateGrammarBackend::test_custom_backend_receives_args PASSED                                                                                   [ 77%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestCreateGrammarBackend::test_custom_backend_skips_reasoner_wrapping PASSED                                                                         [ 79%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestCreateGrammarBackend::test_custom_registered_backend PASSED                                                                                      [ 81%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestCreateGrammarBackend::test_invalid_backend_raises PASSED                                                                                         [ 83%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestCreateGrammarBackend::test_llguidance_backend PASSED                                                                                             [ 85%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestCreateGrammarBackend::test_no_reasoner_wrapping_without_reasoning_parser PASSED                                                                  [ 87%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestCreateGrammarBackend::test_no_reasoner_wrapping_without_think_end_id PASSED                                                                      [ 88%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestCreateGrammarBackend::test_none_backend_returns_none PASSED                                                                                      [ 90%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestCreateGrammarBackend::test_outlines_backend PASSED                                                                                               [ 92%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestCreateGrammarBackend::test_reasoner_wrapping_on_builtin_backend PASSED                                                                           [ 94%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestCreateGrammarBackend::test_xgrammar_backend PASSED                                                                                               [ 96%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestCreateGrammarBackend::test_xgrammar_eos_none PASSED                                                                                              [ 98%]
test/registered/unit/constrained/test_base_grammar_backend.py::TestCreateGrammarBackend::test_xgrammar_unsupported_tokenizer_falls_back_to_none PASSED                                                              [100%]

==================================================================================================== warnings summary =====================================================================================================
.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1428
  /root/sglang/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1428: PytestConfigWarning: Unknown config option: asyncio_mode
  
    self._warn_or_fail_if_strict(f"Unknown config option: {key}\n")

<frozen importlib._bootstrap>:488
  <frozen importlib._bootstrap>:488: DeprecationWarning: builtin type SwigPyPacked has no __module__ attribute

<frozen importlib._bootstrap>:488
  <frozen importlib._bootstrap>:488: DeprecationWarning: builtin type SwigPyObject has no __module__ attribute

python/sglang/srt/layers/attention/fla/utils.py:212
  /root/sglang/python/sglang/srt/layers/attention/fla/utils.py:212: UserWarning: Triton is not supported on current platform, roll back to CPU.
    warnings.warn(

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
============================================================================================= 54 passed, 4 warnings in 4.79s ==============================================================================================
# command
python -m pytest test/registered/unit/constrained/test_reasoner_grammar_backend.py -v
# output
=================================================================================================== test session starts ===================================================================================================
platform linux -- Python 3.12.3, pytest-9.0.2, pluggy-1.6.0 -- /root/sglang/.venv/bin/python
cachedir: .pytest_cache
rootdir: /root/sglang/test
configfile: pytest.ini
plugins: cov-7.0.0, anyio-4.12.1
collected 43 items                                                                                                                                                                                                        

test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectStateTransitions::test_initial_state_thinking PASSED                                                                    [  2%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectStateTransitions::test_rollback_state_at_boundary PASSED                                                                [  4%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectStateTransitions::test_rollback_state_during_thinking PASSED                                                            [  6%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectStateTransitions::test_rollback_state_from_post_thinking PASSED                                                         [  9%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectStateTransitions::test_think_end_after_thinking_already_ended PASSED                                                    [ 11%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectStateTransitions::test_transfer_state_during_thinking PASSED                                                            [ 13%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectStateTransitions::test_transfer_state_increments_after_thinking PASSED                                                  [ 16%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectStateTransitions::test_transfer_state_think_end_token PASSED                                                            [ 18%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectAcceptToken::test_accept_after_thinking_calls_grammar PASSED                                                            [ 20%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectAcceptToken::test_accept_during_thinking_skips_grammar PASSED                                                           [ 23%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectAcceptToken::test_accept_sequence_through_thinking_and_generation PASSED                                                [ 25%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectAcceptToken::test_accept_think_end_token PASSED                                                                         [ 27%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectRollback::test_accept_then_rollback_roundtrip PASSED                                                                    [ 30%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectRollback::test_rollback_across_boundary PASSED                                                                          [ 32%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectRollback::test_rollback_during_thinking PASSED                                                                          [ 34%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectRollback::test_rollback_exactly_to_boundary PASSED                                                                      [ 37%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectRollback::test_rollback_far_beyond_all_tokens PASSED                                                                    [ 39%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectRollback::test_rollback_within_generation PASSED                                                                        [ 41%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectRollback::test_rollback_zero PASSED                                                                                     [ 44%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectVocabMask::test_allocate_delegates PASSED                                                                               [ 46%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectVocabMask::test_fill_after_thinking_delegates PASSED                                                                    [ 48%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectVocabMask::test_fill_at_think_end_boundary PASSED                                                                       [ 51%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectVocabMask::test_fill_during_thinking_skips PASSED                                                                       [ 53%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectVocabMask::test_fill_well_into_generation PASSED                                                                        [ 55%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectVocabMask::test_move_delegates PASSED                                                                                   [ 58%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectDelegation::test_apply_vocab_mask_property PASSED                                                                       [ 60%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectDelegation::test_copy_creates_new_wrapper PASSED                                                                        [ 62%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectDelegation::test_copy_does_not_share_state PASSED                                                                       [ 65%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectDelegation::test_finished_getter_delegates PASSED                                                                       [ 67%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectDelegation::test_finished_setter_delegates PASSED                                                                       [ 69%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectDelegation::test_is_terminated_delegates PASSED                                                                         [ 72%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectDelegation::test_jump_and_retokenize_delegates PASSED                                                                   [ 74%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectDelegation::test_jump_forward_str_state_delegates PASSED                                                                [ 76%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectDelegation::test_try_jump_forward_delegates PASSED                                                                      [ 79%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectMaybeInitReasoning::test_reasoning_false_skips_thinking PASSED                                                          [ 81%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectMaybeInitReasoning::test_reasoning_toggle PASSED                                                                        [ 83%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarObjectMaybeInitReasoning::test_reasoning_true_sets_thinking PASSED                                                            [ 86%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarBackend::test_inherits_base_backend PASSED                                                                                    [ 88%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarBackend::test_inits_no_reasoning_on_wrapped PASSED                                                                            [ 90%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarBackend::test_inits_reasoning_on_wrapped PASSED                                                                               [ 93%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarBackend::test_passes_through_invalid_grammar PASSED                                                                           [ 95%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarBackend::test_passes_through_none PASSED                                                                                      [ 97%]
test/registered/unit/constrained/test_reasoner_grammar_backend.py::TestReasonerGrammarBackend::test_wraps_valid_grammar PASSED                                                                                      [100%]

==================================================================================================== warnings summary =====================================================================================================
.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1428
  /root/sglang/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1428: PytestConfigWarning: Unknown config option: asyncio_mode
  
    self._warn_or_fail_if_strict(f"Unknown config option: {key}\n")

<frozen importlib._bootstrap>:488
  <frozen importlib._bootstrap>:488: DeprecationWarning: builtin type SwigPyPacked has no __module__ attribute

<frozen importlib._bootstrap>:488
  <frozen importlib._bootstrap>:488: DeprecationWarning: builtin type SwigPyObject has no __module__ attribute

python/sglang/srt/layers/attention/fla/utils.py:212
  /root/sglang/python/sglang/srt/layers/attention/fla/utils.py:212: UserWarning: Triton is not supported on current platform, roll back to CPU.
    warnings.warn(

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
============================================================================================= 43 passed, 4 warnings in 3.80s ==============================================================================================
# command
python -m pytest test/registered/unit/constrained/test_grammar_manager.py -v
# output
=================================================================================================== test session starts ===================================================================================================
platform linux -- Python 3.12.3, pytest-9.0.2, pluggy-1.6.0 -- /root/sglang/.venv/bin/python
cachedir: .pytest_cache
rootdir: /root/sglang/test
configfile: pytest.ini
plugins: cov-7.0.0, anyio-4.12.1
collected 33 items                                                                                                                                                                                                        

test/registered/unit/constrained/test_grammar_manager.py::TestGrammarManagerInit::test_clear_no_backend PASSED                                                                                                      [  3%]
test/registered/unit/constrained/test_grammar_manager.py::TestGrammarManagerInit::test_clear_resets_backend PASSED                                                                                                  [  6%]
test/registered/unit/constrained/test_grammar_manager.py::TestGrammarManagerInit::test_init_skip_tokenizer PASSED                                                                                                   [  9%]
test/registered/unit/constrained/test_grammar_manager.py::TestGrammarManagerInit::test_init_with_backend PASSED                                                                                                     [ 12%]
test/registered/unit/constrained/test_grammar_manager.py::TestGrammarManagerInit::test_len_and_has_waiting PASSED                                                                                                   [ 15%]
test/registered/unit/constrained/test_grammar_manager.py::TestProcessReqWithGrammar::test_cache_hit_invalid_grammar_aborts PASSED                                                                                   [ 18%]
test/registered/unit/constrained/test_grammar_manager.py::TestProcessReqWithGrammar::test_cache_hit_returns_false PASSED                                                                                            [ 21%]
test/registered/unit/constrained/test_grammar_manager.py::TestProcessReqWithGrammar::test_ebnf_cache_miss PASSED                                                                                                    [ 24%]
test/registered/unit/constrained/test_grammar_manager.py::TestProcessReqWithGrammar::test_has_waiting_grammars_after_enqueue PASSED                                                                                 [ 27%]
test/registered/unit/constrained/test_grammar_manager.py::TestProcessReqWithGrammar::test_json_schema_cache_miss PASSED                                                                                             [ 30%]
test/registered/unit/constrained/test_grammar_manager.py::TestProcessReqWithGrammar::test_json_takes_priority_over_other_constraints PASSED                                                                         [ 33%]
test/registered/unit/constrained/test_grammar_manager.py::TestProcessReqWithGrammar::test_no_backend_aborts PASSED                                                                                                  [ 36%]
test/registered/unit/constrained/test_grammar_manager.py::TestProcessReqWithGrammar::test_no_constraint_returns_false PASSED                                                                                        [ 39%]
test/registered/unit/constrained/test_grammar_manager.py::TestProcessReqWithGrammar::test_regex_cache_miss PASSED                                                                                                   [ 42%]
test/registered/unit/constrained/test_grammar_manager.py::TestProcessReqWithGrammar::test_require_reasoning_forwarded_to_backend PASSED                                                                             [ 45%]
test/registered/unit/constrained/test_grammar_manager.py::TestProcessReqWithGrammar::test_structural_tag_cache_miss PASSED                                                                                          [ 48%]
test/registered/unit/constrained/test_grammar_manager.py::TestAbortRequests::test_abort_all PASSED                                                                                                                  [ 51%]
test/registered/unit/constrained/test_grammar_manager.py::TestAbortRequests::test_abort_by_rid_prefix PASSED                                                                                                        [ 54%]
test/registered/unit/constrained/test_grammar_manager.py::TestAbortRequests::test_abort_empty_queue PASSED                                                                                                          [ 57%]
test/registered/unit/constrained/test_grammar_manager.py::TestAbortRequests::test_abort_non_matching_rid PASSED                                                                                                     [ 60%]
test/registered/unit/constrained/test_grammar_manager.py::TestAbortRequests::test_abort_prefix_match PASSED                                                                                                         [ 63%]
test/registered/unit/constrained/test_grammar_manager.py::TestGetReadyGrammarRequests::test_aborted_req_removed_from_queue PASSED                                                                                   [ 66%]
test/registered/unit/constrained/test_grammar_manager.py::TestGetReadyGrammarRequests::test_empty_queue PASSED                                                                                                      [ 69%]
test/registered/unit/constrained/test_grammar_manager.py::TestGetReadyGrammarRequests::test_future_exception_propagates PASSED                                                                                      [ 72%]
test/registered/unit/constrained/test_grammar_manager.py::TestGetReadyGrammarRequests::test_invalid_grammar_aborts_req PASSED                                                                                       [ 75%]
test/registered/unit/constrained/test_grammar_manager.py::TestGetReadyGrammarRequests::test_mixed_ready_and_pending PASSED                                                                                          [ 78%]
test/registered/unit/constrained/test_grammar_manager.py::TestGetReadyGrammarRequests::test_multi_rank_sync_intersects_ready_unions_failed PASSED                                                                   [ 81%]
test/registered/unit/constrained/test_grammar_manager.py::TestGetReadyGrammarRequests::test_multi_rank_sync_unions_failed PASSED                                                                                    [ 84%]
test/registered/unit/constrained/test_grammar_manager.py::TestGetReadyGrammarRequests::test_multiple_reqs_sharing_same_future PASSED                                                                                [ 87%]
test/registered/unit/constrained/test_grammar_manager.py::TestGetReadyGrammarRequests::test_pending_future_stays_in_queue PASSED                                                                                    [ 90%]
test/registered/unit/constrained/test_grammar_manager.py::TestGetReadyGrammarRequests::test_progressive_timeout PASSED                                                                                              [ 93%]
test/registered/unit/constrained/test_grammar_manager.py::TestGetReadyGrammarRequests::test_ready_future_returns_req PASSED                                                                                         [ 96%]
test/registered/unit/constrained/test_grammar_manager.py::TestGetReadyGrammarRequests::test_timeout_aborts_req PASSED                                                                                               [100%]

==================================================================================================== warnings summary =====================================================================================================
.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1428
  /root/sglang/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py:1428: PytestConfigWarning: Unknown config option: asyncio_mode
  
    self._warn_or_fail_if_strict(f"Unknown config option: {key}\n")

<frozen importlib._bootstrap>:488
  <frozen importlib._bootstrap>:488: DeprecationWarning: builtin type SwigPyPacked has no __module__ attribute

<frozen importlib._bootstrap>:488
  <frozen importlib._bootstrap>:488: DeprecationWarning: builtin type SwigPyObject has no __module__ attribute

python/sglang/srt/layers/attention/fla/utils.py:212
  /root/sglang/python/sglang/srt/layers/attention/fla/utils.py:212: UserWarning: Triton is not supported on current platform, roll back to CPU.
    warnings.warn(

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
============================================================================================= 33 passed, 4 warnings in 4.08s ==============================================================================================

Checklist

Review Process

  1. Ping Merge Oncalls to start the PR flow. See the PR Merge Process.
  2. Get approvals from CODEOWNERS and other reviewers.
  3. Trigger CI tests with comments or contact authorized users to do so.
    • /tag-run-ci-label, /rerun-failed-ci, /tag-and-rerun-ci
  4. After green CI and required approvals, ask Merge Oncalls to merge.

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the test coverage for the srt/constrained module by introducing a suite of unit tests for its core components. These new tests validate the functionality of grammar backend operations, the grammar manager's request processing and lifecycle, the specialized reasoner grammar backend's state management, and utility functions for structural tag parsing. The added tests aim to improve the reliability and maintainability of the constrained generation features.

Highlights

  • Unit Tests for Base Grammar Backend: New unit tests were added for sglang.srt.constrained.base_grammar_backend, covering GrammarStats defaults and custom values, BaseGrammarObject properties and abstract methods, InvalidGrammarObject behavior, and BaseGrammarBackend caching, dispatch, and thread pool execution. This also includes tests for create_grammar_backend and register_grammar_backend.
  • Unit Tests for Grammar Manager: Comprehensive unit tests were introduced for sglang.srt.constrained.grammar_manager, validating GrammarManager initialization, queue management, request processing with various constraint types (JSON, regex, EBNF, structural tags), handling of cache hits and invalid grammars, request abortion, and retrieval of ready grammar requests.
  • Unit Tests for Reasoner Grammar Backend: Unit tests were added for sglang.srt.constrained.reasoner_grammar_backend, focusing on ReasonerGrammarObject's state transitions during thinking and post-thinking phases, token acceptance, rollback logic, vocabulary mask gating, and method delegation. Tests also cover ReasonerGrammarBackend's dispatch wrapping and handling of different grammar types.
  • Unit Tests for Constrained Utilities: Unit tests were added for sglang.srt.constrained.utils, specifically for the is_legacy_structural_tag function, ensuring correct detection of legacy and new structural tag formats and proper error handling for malformed inputs.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@vaibhawvipul
Copy link
Copy Markdown
Contributor Author

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a comprehensive suite of unit tests for the srt/constrained module, significantly improving test coverage and ensuring the reliability of the new features. The tests are well-structured, thorough, and follow best practices for isolation and mocking. I've identified a couple of opportunities to enhance the test suite further by adding coverage for specific concurrent scenarios, which are critical for the robustness of the grammar caching and request management logic. My detailed suggestions are in the comments below.

Comment thread test/registered/unit/constrained/test_base_grammar_backend.py Outdated
Comment thread test/registered/unit/constrained/test_grammar_manager.py
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a comprehensive set of unit tests for the srt/constrained module, covering base_grammar_backend, grammar_manager, reasoner_grammar_backend, and utility functions. The tests are well-structured, thorough, and address numerous edge cases, which significantly enhances the robustness and maintainability of the constrained generation functionality. I have one minor suggestion to improve code cleanliness.

Comment thread test/registered/unit/constrained/test_grammar_manager.py Outdated
@vaibhawvipul
Copy link
Copy Markdown
Contributor Author

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a comprehensive set of unit tests for the srt/constrained module, significantly improving test coverage. The tests are well-structured and cover a wide range of scenarios, including object states, caching, error handling, and concurrency-related logic. My review focuses on enhancing the robustness and correctness of the tests themselves. I've identified a minor improvement for thread pool shutdown to ensure deterministic test cleanup and a more critical issue in a test case that appears to validate incorrect behavior regarding shared state, which could mask a potential bug. Addressing these points will further strengthen the quality of the test suite.

Comment thread test/registered/unit/constrained/test_grammar_manager.py Outdated
Comment thread test/registered/unit/constrained/test_base_grammar_backend.py Outdated
vaibhawvipul and others added 3 commits March 20, 2026 15:56
…r_requests

Each request sharing a Future now gets its own copy via grammar.copy(),
preventing shared-state issues between concurrent requests. The original
grammar object is stored in the cache, and each request receives an
independent copy.
@vaibhawvipul vaibhawvipul changed the title Add unit tests for srt/constrained module [Test] Add unit tests for srt/constrained module Mar 20, 2026
@vaibhawvipul
Copy link
Copy Markdown
Contributor Author

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request introduces a minor but important correction in grammar_manager.py to ensure proper state isolation when caching grammar objects. Previously, the cached object might have been directly modified if req.grammar was later altered, but now a copy is correctly cached and a separate copy is assigned to the request. This change improves the robustness of the grammar caching mechanism. Additionally, the PR adds comprehensive unit tests for base_grammar_backend.py, grammar_manager.py, reasoner_grammar_backend.py, and utils.py. These new tests significantly enhance the code coverage and verify the correct behavior of various components, including grammar object states, caching logic, dispatch routing, and multi-rank synchronization. The new tests are well-structured and cover a wide range of scenarios, including edge cases and error handling. Overall, these changes are a valuable improvement to the codebase, enhancing both correctness and testability.

@vaibhawvipul vaibhawvipul marked this pull request as ready for review March 20, 2026 11:18
Comment thread test/registered/unit/constrained/__init__.py Outdated
@ispobock
Copy link
Copy Markdown
Collaborator

/tag-and-rerun-ci

@vaibhawvipul vaibhawvipul requested a review from ispobock March 20, 2026 16:37
Comment thread test/registered/unit/constrained/test_base_grammar_backend.py Outdated
Comment thread test/registered/unit/constrained/test_base_grammar_backend.py Outdated
Comment thread python/sglang/srt/constrained/grammar_manager.py Outdated
@vaibhawvipul vaibhawvipul requested a review from ispobock March 22, 2026 07:18
@ispobock ispobock merged commit 6d2e115 into sgl-project:main Mar 23, 2026
89 of 107 checks passed
@vaibhawvipul vaibhawvipul deleted the unit-tests-constrained branch March 23, 2026 17:00
adityavaid pushed a commit to adityavaid/sglang that referenced this pull request Mar 24, 2026
0-693 pushed a commit to 0-693/sglang that referenced this pull request Mar 25, 2026
JustinTong0323 pushed a commit to JustinTong0323/sglang that referenced this pull request Apr 7, 2026
yhyang201 pushed a commit to yhyang201/sglang that referenced this pull request Apr 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants