Skip to content

[#222] test: 파서 property 기반 테스트 도입 (+ CR 공백 처리 1줄) - #242

Merged
myyrakle merged 5 commits into
myyrakle:masterfrom
DPS0340:test/222-property-based-parser
Aug 31, 2026
Merged

[#222] test: 파서 property 기반 테스트 도입 (+ CR 공백 처리 1줄)#242
myyrakle merged 5 commits into
myyrakle:masterfrom
DPS0340:test/222-property-based-parser

Conversation

@DPS0340

@DPS0340 DPS0340 commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

resolves: #222 (Phase 1-2)

⚠️ 이 PR은 #239 위에 쌓여 있습니다. #239가 먼저 머지되어야 합니다.

처음 올렸을 때 이 PR은 EOF 경계 수정을 자체적으로 들고 있었는데, #239와 같은 버그를 다른 이름으로 고치는 중복이었습니다(last_read_consumed vs has_pending_char — 같은 불리언의 반대 극성). 그래서 겹치는 부분을 전부 걷어내고 #239 위에 재구성했습니다. 이제 두 PR은 충돌하지 않습니다.

요약

이슈의 Phase 1(인프라)과 Phase 2(파서) 를 구현했습니다. Phase 3-5(인덱스/옵티마이저/스토리지)는 범위가 크고 진행 중인 인덱스 작업과 파일이 겹쳐서 후속으로 분리했습니다.

#239를 제외하면 이 PR의 실제 변경은 소스 한 줄과 테스트입니다.

src/engine/lexer/tokenizer.rs   | 5 +-   ← \r 한 줄
tests (property + eof_lookahead) | +186
Cargo.toml (proptest dev-dep)    | 5 +-

유일한 소스 변경: CR(\r)이 공백으로 인식되지 않음

is_whitespace' ', \n, \t만 처리해서, CRLF 줄바꿈이 든 SQL이 unexpected character: '\r'로 실패했습니다. Windows에서 저장한 스크립트나 CRLF를 보내는 클라이언트가 영향을 받습니다.

 pub fn is_whitespace(&self) -> bool {
-    self.last_char == ' ' || self.last_char == '\n' || self.last_char == '\t'
+    self.last_char == ' '
+        || self.last_char == '\n'
+        || self.last_char == '\t'
+        || self.last_char == '\r'
 }

속성 테스트를 돌리다 발견한 버그입니다. 이 한 줄을 되돌리면 whitespace_only_input_yields_no_statements가 실제로 실패하는 것을 확인했습니다:

engine::parser::test::property::whitespace_only_input_yields_no_statements --- FAILED
test result: FAILED. 323 passed; 1 failed

추가한 테스트

engine/parser/test/property.rs — proptest 기반 속성 5종

  • 임의 입력에 대해 파서가 패닉하지 않음
  • 공백만 있는 입력은 구문을 만들지 않음
  • 왕복(파싱 → 재출력 → 재파싱) 일치
  • 토크나이저가 임의 입력에서 종료함
  • 유효한 SQL 생성 후 파싱 성공

engine/lexer/test/eof_lookahead.rs — #239가 고친 EOF 경계를 입력 끝 연산자(-, /, <, >, !)별로 고정하는 회귀 테스트입니다. 수정 자체는 #239 소유이고, 여기서는 그 동작이 유지되는지만 검증합니다.

확인

  • 이 브랜치 단독: 657개 통과
  • master 위에 열린 PR 7건 전부 순서대로 머지: 충돌 0, 705개 통과

Summary by CodeRabbit

  • 테스트

    • 렉서와 파서에 프로퍼티 기반 테스트를 추가해 다양한 입력에서의 안정성, 결정성, 공백 처리 일관성을 검증합니다.
    • 입력 끝의 연산자, CRLF 공백, 인용 식별자 등 경계 조건에 대한 회귀 테스트를 추가했습니다.
    • 과거 실패 사례를 재현할 수 있는 회귀 테스트 자료를 포함했습니다.
  • 스타일

    • 공백 문자 판정 로직의 가독성을 개선했습니다.

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b84457fe-c003-4495-85d9-e02df0b1b09b

📥 Commits

Reviewing files that changed from the base of the PR and between 218ab51 and b003aa9.

📒 Files selected for processing (1)
  • src/engine/parser/test/property.rs

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


Walkthrough

proptest 개발 의존성과 파서 프로퍼티 테스트를 추가했습니다. EOF 인접 연산자, CRLF 공백, 인용 식별자를 검증하는 렉서 회귀 테스트도 추가했습니다. 실패한 프로퍼티 테스트의 회귀 시드를 저장합니다.

Changes

렉서 및 파서 테스트

Layer / File(s) Summary
프로퍼티 테스트 인프라 구성
Cargo.toml, src/engine/parser/test/mod.rs
proptest 개발 의존성을 추가하고 파서 프로퍼티 테스트 모듈을 등록했습니다.
렉서 EOF 회귀 검증
src/engine/lexer/tokenizer.rs, src/engine/lexer/test/eof_lookahead.rs
EOF 인접 단일·조합 연산자, CRLF 공백, 닫히지 않은 인용 식별자의 토큰화 동작을 검증합니다.
파서 프로퍼티 테스트
src/engine/parser/test/property.rs, proptest-regressions/engine/parser/test/property.txt
임의 입력의 panic 방지, 결정성, 공백 처리, 유효·무효 SQL 및 빈 입력 속성을 검증하고 실패 시드를 기록합니다.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to b003a

The PR adds CR whitespace support and expands parser/lexer regression coverage without introducing an actionable merge-blocking risk; it is merge-ready after normal checks and review.

Poem

당근 곁에 테스트가 톡,
토끼는 무작위를 살핍니다.
EOF와 CRLF를 확인하고,
파서의 결과를 기록합니다.
작은 시드 하나 남기면,
실패 사례를 다시 찾습니다.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning [ #222 ] proptest 의존성과 파서 property 테스트를 추가하고, 패닉 방지와 오류 처리를 검증했습니다. 그러나 이슈의 Phase 2 요구사항인 SQL AST round-trip 검증과 연산자 우선순위 구조 검증은 변경 요약에서 확인되지 않습니다. 유효한 SQL에 대해 parse(unparse(parse(sql))) 결과의 의미적 동등성을 검증하는 property를 추가하십시오. AND/OR/비교 연산자의 get_precedence에 따른 파싱 트리 구조를 검증하는 property도 추가하십시오.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 주요 변경인 파서 property-based 테스트 도입을 명확히 설명하며, CR 공백 처리 변경도 포함합니다.
Out of Scope Changes check ✅ Passed 변경 사항은 [ #222 ]의 property 테스트 도입과 관련된 테스트 인프라, 파서 테스트, CR 공백 처리 및 EOF 회귀 테스트로 제한됩니다. 관련 없는 코드 변경은 확인되지 않습니다.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 12 functions across 3 files.
  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@DPS0340

DPS0340 commented Jul 25, 2026

Copy link
Copy Markdown
Contributor Author

리뷰 편의를 위해 확인한 내용 공유드립니다.

현재 열려 있는 제 PR 3건(#242, #243, #244)은 건드리는 파일이 서로 겹치지 않습니다.

PR 파일
#242 lexer/tokenizer.rs, 파서/렉서 테스트, Cargo.toml
#243 actions/dml/{insert,scan}.rs, engine/mod.rs, wal/types.rs
#244 pgwire/protocol/connection_codec.rs

실제로 셋을 모두 master 위에 합쳐서 확인했고, 충돌 없이 머지되며 테스트도 통과합니다:

merge #242 -> ok
merge #243 -> ok
merge #244 -> ok

cargo test: 665 passed / 0 failed

어떤 순서로 병합하셔도 되고, 하나만 먼저 가져가셔도 나머지가 깨지지 않습니다. 부담되시면 제일 작은 #244(파일 1개)부터 보셔도 좋습니다.

각 PR이 고치는 내용은 이렇습니다:

세 건 모두 수정을 무력화하면 새 테스트가 실제로 실패하는 것을 확인했습니다.

@DPS0340

DPS0340 commented Jul 25, 2026

Copy link
Copy Markdown
Contributor Author

현재 열려 있는 제 PR 6건의 병합 가능성을 실제로 확인했습니다.

master 위에 6건을 전부 순서대로 머지해본 결과, 충돌 없이 모두 적용되고 테스트도 통과합니다:

#242 test/222-property-based-parser     -> ok
#243 fix/236-idempotent-insert-replay   -> ok
#244 fix/pgwire-startup-hardening       -> ok
#245 fix/pgwire-startup-boundary        -> ok
#246 fix/arithmetic-overflow-div-zero   -> ok
#247 fix/unary-neg-overflow             -> ok

cargo test: 681 passed / 0 failed

두 곳만 순서가 있습니다:

나머지는 순서와 무관하고, 하나만 먼저 가져가셔도 나머지가 깨지지 않습니다.

각 PR이 고치는 내용입니다:

PR 내용
#242 토크나이저 무한 루프 3종 (-///</>/! 끝, 안 닫힌 ", CRLF)
#243 크래시 복구 시 INSERT 중복 적용
#244 조작된 길이 필드로 인한 원격 패닉 (인증 이전)
#245 startup 파라미터에 후속 메시지가 섞여 들어감
#246 1/0 및 정수 오버플로 패닉
#247 -i64::MIN 패닉

전부 수정을 되돌리면 새 테스트가 실제로 실패하는 것을 확인했습니다. 부담되시면 파일 1개짜리(#244, #246, #247)부터 보셔도 됩니다.

@DPS0340
DPS0340 force-pushed the test/222-property-based-parser branch 2 times, most recently from c8747f3 to c5ae705 Compare July 25, 2026 15:55
@DPS0340 DPS0340 changed the title [#222] test: property-based 파서 테스트 도입 + 토크나이저 무한 루프 3건 수정 [#222] test: 파서 property 기반 테스트 도입 (+ CR 공백 처리 1줄) Jul 25, 2026
@DPS0340
DPS0340 force-pushed the test/222-property-based-parser branch from c5ae705 to 42ee295 Compare July 25, 2026 17:13
DPS0340 added a commit to DPS0340/rrdb that referenced this pull request Jul 25, 2026
이전 실행이 코드와 무관한 러너 이슈로 실패했습니다:
  error: binary `cargo-tarpaulin` already exists in destination

동일 잡이 myyrakle#242/myyrakle#245/#246에서는 SUCCESS입니다.
@DPS0340
DPS0340 force-pushed the test/222-property-based-parser branch from 42ee295 to 6c99986 Compare July 26, 2026 04:02
DPS0340 added a commit to DPS0340/rrdb that referenced this pull request Jul 26, 2026
이전 실행이 코드와 무관한 러너 이슈로 실패했습니다:
  error: binary `cargo-tarpaulin` already exists in destination

동일 잡이 myyrakle#242/myyrakle#245/#246에서는 SUCCESS입니다.
@DPS0340
DPS0340 force-pushed the test/222-property-based-parser branch from 6c99986 to f123302 Compare July 26, 2026 04:05

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/engine/parser/test/property.rs`:
- Around line 44-58: Update parser_never_panics_on_arbitrary_text and
parser_never_panics_on_sql_like_input to assert try_parse results instead of
discarding them. For valid SQL cases, assert successful parsing with is_ok();
for intentionally malformed cases, assert an error with the expected ErrorKind.
Preserve the panic-safety coverage while validating both successful parsing and
error-return contracts.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 10ee6158-72c6-4252-b1cd-dc6c6fd0ee0b

📥 Commits

Reviewing files that changed from the base of the PR and between 0c917a5 and 6c99986.

📒 Files selected for processing (6)
  • Cargo.toml
  • proptest-regressions/engine/parser/test/property.txt
  • src/engine/lexer/test/eof_lookahead.rs
  • src/engine/lexer/tokenizer.rs
  • src/engine/parser/test/mod.rs
  • src/engine/parser/test/property.rs

Comment on lines +44 to +58
/// The parser must never panic, however malformed the input. Any input it
/// cannot handle has to come back as an `Err`, not an abort — a panic here
/// would take down the connection task that is parsing the statement.
#[test]
fn parser_never_panics_on_arbitrary_text(sql in arbitrary_sql_text()) {
let _ = try_parse(&sql);
}

/// Same invariant, but with inputs built from real SQL keywords so the
/// generator reaches the statement parsers rather than failing at the
/// tokenizer.
#[test]
fn parser_never_panics_on_sql_like_input(sql in sql_like_fragment()) {
let _ = try_parse(&sql);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

파싱 결과를 폐기하지 말고 성공·실패 계약을 단언하세요.

try_parse 결과를 버리므로 잘못된 SQL이 묵시적으로 성공해도, 유효 SQL이 실패해도 통과합니다. 유효 SQL 전략에는 is_ok()를, 의도적으로 잘못된 SQL 전략에는 기대한 ErrorKind의 오류를 단언하는 속성을 추가하세요. 이는 PR 목표의 “유효 SQL 파싱 성공” 및 “잘못된 SQL 오류 반환”을 현재 검증하지 못합니다.

Also applies to: 75-97

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/engine/parser/test/property.rs` around lines 44 - 58, Update
parser_never_panics_on_arbitrary_text and parser_never_panics_on_sql_like_input
to assert try_parse results instead of discarding them. For valid SQL cases,
assert successful parsing with is_ok(); for intentionally malformed cases,
assert an error with the expected ErrorKind. Preserve the panic-safety coverage
while validating both successful parsing and error-return contracts.

@DPS0340
DPS0340 force-pushed the test/222-property-based-parser branch 3 times, most recently from f090fb6 to f799294 Compare July 26, 2026 06:28
@DPS0340

DPS0340 commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

#250이 머지되면서 behind가 되어 현재 master(966b402) 위로 리베이스했습니다. 충돌 없었고 CI 초록입니다.

리베이스 후 로컬에서 전체 테스트를 다시 돌린 결과입니다:

cargo test --quiet   →  0 failed

참고로 #250이 고친 tarpaulin 캐시 경합이 바로 이 PR들의 coverage 잡을 간헐적으로 빨갛게 만들던 원인이었습니다. 이제 그 노이즈 없이 판단하실 수 있습니다.

@DPS0340
DPS0340 force-pushed the test/222-property-based-parser branch from f799294 to d82bb50 Compare July 26, 2026 06:43
@DPS0340

DPS0340 commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

이 PR을 다시 보다가 제가 쓴 주석이 사실이 아니라는 것을 발견해서 고쳤습니다 (d82bb50).

무엇이 잘못됐나

sql_like_fragment에 이렇게 적어뒀습니다:

실제 SQL 키워드에서 뽑은 조각들이라, 생성기가 예산을 토크나이저에서 바로 거부되는 입력 대신 파서 깊숙이 도달하는 입력에 씁니다.

측정해보니 반대였습니다. 1024건을 생성해 실제로 문장이 파싱된 개수를 세면:

sql_like_fragment   nonempty_stmts = 0~1 / 1024

키워드를 균등하게 섞으면 SELECT/INSERT 같은 문장을 여는 키워드로 시작하는 경우가 거의 없습니다. 그러면 파서는 빈 문장 목록을 돌려주고 끝나서, 그 뒤 코드에는 진입조차 하지 못합니다. panic 불변식이 토크나이저 근처에서만 검증되고 있었던 셈입니다.

생성된 입력을 찍어보면 바로 보입니다:

"OR BY VALUES 'x' foo INTO DROP 'x' GROUP"   -> stmts=0
"SET WHERE BY ("                              -> stmts=0
"DATABASE GROUP"                              -> stmts=0
"AS"                                          -> stmts=0

고친 것

  1. 첫 토큰을 고정 — 문장을 여는 키워드 중에서만 뽑습니다. 0~1 → 10~15
  2. mutated_statement 생성기 추가 — 정상 문장에서 출발해 토큰 하나를 임의 위치에 주입합니다. 파서가 문장 종류를 확정한 뒤에 예상치 못한 토큰을 만나므로 본문 코드에 도달합니다. nonempty_stmts = ~480 / 1024

1번만으로는 부족했습니다. 1.5%도 여전히 낮아서, 문장 본문을 제대로 훑으려면 유효한 문장에서 출발하는 쪽이 맞았습니다.

새 테스트가 실제로 무언가를 잡는지

주장하지 않고 확인했습니다. parse_where에 panic을 심었습니다 — 유효한 SELECT 문 안에서만 도달하는 지점입니다:

parser_never_panics_on_arbitrary_text          ok      <- 놓침
parser_never_panics_on_sql_like_input          ok      <- 놓침
parsing_is_deterministic                       ok      <- 놓침
surrounding_whitespace_is_insignificant        ok      <- 놓침
whitespace_only_input_yields_no_statements     ok      <- 놓침

parser_never_panics_on_mutated_statement       FAILED  <- 잡음
parsing_a_mutated_statement_is_deterministic   FAILED  <- 잡음

기존 5개는 전부 통과하고 신규 2개만 실패합니다. 이게 이 커밋의 요지입니다.

검증

cargo test    683 passed / 0 failed
cargo clippy  경고 70건 — master와 동일

리베이스도 함께 했습니다. #250 머지 후 master 위에서 4개 체크 모두 초록불입니다.

(cargo clippy --all-targetsbenches/index_benchmark.rs 경로 오류로 실패하는데, master에서도 동일하게 재현됩니다. 이 PR과는 무관합니다.)

@DPS0340

DPS0340 commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

바로 앞 코멘트에서 한 말을 정정합니다. 제가 틀렸습니다.

이렇게 적었습니다:

cargo clippy --all-targetsbenches/index_benchmark.rs 경로 오류로 실패하는데, master에서도 동일하게 재현됩니다. 이 PR과는 무관합니다.

master에서 재현되지 않습니다. 제가 만든 문제였습니다.

각 브랜치의 Cargo.toml을 직접 확인했습니다:

master                          path = "./src/benches/index_benchmark.rs"   정상
test/222-property-based-parser  path = "./benches/index_benchmark.rs"       <- 이 PR
test/251-wal-frame-integrity    path = "./src/benches/index_benchmark.rs"   정상
feat/232-index-free-list        path = "./src/benches/index_benchmark.rs"   정상
fix/unary-neg-overflow          path = "./src/benches/index_benchmark.rs"   정상

git log -S로 추적하니 제 첫 커밋 f090fb6이 범인이었습니다. 이 PR은 proptest dev-dependency만 추가하면 되는데, 벤치 경로까지 건드렸습니다. 실제 파일은 src/benches/index_benchmark.rs에 있습니다.

40bae65로 원복했습니다. 이제 --all-targets가 정상 완료되고 경고 수는 73건으로 master와 같습니다.

왜 놓쳤나

master와 비교할 때 --all-targets 없이 cargo clippy --tests로 셌습니다. 그 경로에서는 벤치 타깃이 아예 해석되지 않아 오류가 나타나지 않고, 양쪽 다 70건으로 같게 보였습니다. 오류를 보고도 "기존 문제겠지"라고 넘긴 뒤 그 가정으로 비교 방법을 고른 셈입니다.

두 브랜치에서 같은 명령을 돌렸어야 했습니다. 실패하는 그 명령으로 대조했다면 즉시 드러났을 문제입니다.

앞 코멘트의 나머지 내용(생성기가 파서 본문에 도달하지 못하던 문제와 그 수정)은 그대로 유효합니다.

@DPS0340

DPS0340 commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

반영했습니다 (584b2f0). 지적이 정확했고, 그 지적이 왜 맞는지 실행으로 확인했습니다.

확인한 것

parse()가 무조건 빈 결과를 돌려주도록 바꿔서, "아무것도 파싱하지 않는 파서"를 만들어봤습니다:

parser_never_panics_on_arbitrary_text     ok
parser_never_panics_on_sql_like_input     ok
parser_never_panics_on_mutated_statement  ok
parsing_is_deterministic                  ok
surrounding_whitespace_is_insignificant   ok

전부 통과합니다. 말씀하신 대로 기존 property는 성공 계약을 전혀 검증하지 못하고 있었습니다.

반영

패닉 방지 property 자체는 결과를 버리는 게 맞다고 봤습니다 — 임의 텍스트에 대해 성공/실패를 단정할 수 없고, 그 테스트의 계약은 "패닉하지 않는다" 하나입니다. 대신 빠져 있던 계약을 담당하는 property를 추가했습니다.

  • valid_statement() 생성기 — 구성상 유효한 SQL만 만듭니다. SELECT/INSERT/UPDATE/DELETE/DROP 7가지 형태에 테이블·컬럼·리터럴·세미콜론 유무를 조합합니다.
  • valid_statements_parse_into_exactly_one_statement — 성공 쪽 계약. 파싱되고, 정확히 한 문장을 만드는지 봅니다.
  • a_bare_statement_keyword_is_an_error — 실패 쪽 계약. 키워드만 있고 뒤가 없는 입력은 조용히 빈 결과가 아니라 에러여야 합니다.

ErrorKind까지 단언하는 건 넣지 않았습니다. 현재 파서가 이 경우 LexingErrorParsingError를 입력에 따라 다르게 반환하는데, 그 구분을 지금 고정하면 파서 내부 구조를 테스트가 붙잡게 됩니다. 어느 쪽이 계약인지 정해주시면 그에 맞춰 좁히겠습니다.

새 property가 실제로 무언가를 잡는지

위의 "빈 결과만 돌려주는 파서"를 다시 넣었습니다:

valid_statements_parse_into_exactly_one_statement  FAILED
a_bare_statement_keyword_is_an_error               FAILED
기존 패닉 property 3건                              ok

신규 2건만 뒤집힙니다.

검증

cargo test              687 passed / 0 failed
cargo clippy --all-targets  경고 73건 — master와 동일

@DPS0340
DPS0340 force-pushed the test/222-property-based-parser branch from 584b2f0 to 218ab51 Compare July 26, 2026 13:13

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/engine/parser/test/property.rs`:
- Around line 146-148: Update parsing_a_mutated_statement_is_deterministic and
the related property tests in src/engine/parser/test/property.rs at lines
146-148, 154-162, and 167-175 to compare complete parse outcomes: preserve
successful AST equality and, for Err-Err results, explicitly verify matching
ErrorKind rather than only checking both are errors. Ensure the
whitespace-before/after failure property also validates identical ErrorKind; all
three sites require direct assertion updates.
- Around line 20-25: Expand the arbitrary input strategy in arbitrary_sql_text
so it generates genuinely arbitrary characters, including control characters,
quotes, and unbalanced delimiters, instead of the current restricted ASCII
regex. Use an appropriate proptest strategy such as proptest::char::any() or a
byte-based regex while preserving the String return type and existing
panic-invariant test usage.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: facd1785-4b17-45de-9257-8eee7d7ae849

📥 Commits

Reviewing files that changed from the base of the PR and between 6c99986 and 218ab51.

📒 Files selected for processing (6)
  • Cargo.toml
  • proptest-regressions/engine/parser/test/property.txt
  • src/engine/lexer/test/eof_lookahead.rs
  • src/engine/lexer/tokenizer.rs
  • src/engine/parser/test/mod.rs
  • src/engine/parser/test/property.rs
🚧 Files skipped from review as they are similar to previous changes (4)
  • proptest-regressions/engine/parser/test/property.txt
  • src/engine/lexer/tokenizer.rs
  • src/engine/parser/test/mod.rs
  • src/engine/lexer/test/eof_lookahead.rs

Comment thread src/engine/parser/test/property.rs Outdated
Comment thread src/engine/parser/test/property.rs
@DPS0340
DPS0340 force-pushed the test/222-property-based-parser branch from 218ab51 to 1aa4beb Compare July 27, 2026 03:04

@myyrakle myyrakle left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

리뷰 확인하세요

@DPS0340

DPS0340 commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

CodeRabbit 리뷰 반영했습니다 (b003aa9).

#3652563996 (Minor) — 임의 텍스트 생성 범위
arbitrary_sql_text를 기존 ASCII 정규식에서 proptest::char::any() 기반 vec 전략으로 교체했습니다. 제어문자(NUL, 단독 `\r`), quote, unbalanced delimiter가 실제로 생성됩니다. 1024 케이스에서 패닉 불변성 유지 확인.

#3652563997 (Minor) — Err/Err 결과 비교
결정성 3건(parsing_a_mutated_statement_is_deterministic, parsing_is_deterministic, surrounding_whitespace_is_insignificant)을 완전한 결과 비교로 바꿨습니다:

  • Ok/Ok → AST 디버그 출력 비교 (기존 유지)
  • Err/Err → `ErrorKind` 동일성 비교 (신규)
  • Ok/Err 엇갈림 → 명시적 실패 (기존에는 `prop_assert_eq!(is_ok, is_ok)`가 이걸 잡아줬지만 Err 종류는 놓쳤음)

#3651713224 (Major) 은 커밋 1aa4beb에서 이미 반영된 상태입니다(valid_statements_parse_into_exactly_one_statement + a_bare_statement_keyword_is_an_error).

검증: cargo test 745 passed / 0 failed, clippy 73건(master와 동일).

proptest로 파서 불변식을 검증하는 테스트를 추가합니다.

- 임의 입력에 대해 파서가 패닉하지 않음
- 공백만 있는 입력은 구문을 만들지 않음
- 왕복(파싱 → 재출력 → 재파싱) 일치

테스트를 돌리는 과정에서 `is_whitespace`가 캐리지 리턴(\r)을
공백으로 보지 않아, CRLF 줄바꿈 입력에서 파서가 멈추는 문제를
발견해 함께 고쳤습니다. 이 한 줄을 되돌리면
`whitespace_only_input_yields_no_statements`가 실제로 실패합니다.

EOF 경계 처리는 #239와 중복이라 걷어내고, 이 브랜치를 myyrakle#239 위에
쌓았습니다. #239가 먼저 머지되어야 합니다.
이 PR의 주석에 '생성기가 파서 깊숙이 도달한다'고 적었는데, 측정해보니
사실이 아니었습니다. 1024건을 생성해 실제로 파싱되는 문장 수를 세면:

  sql_like_fragment   nonempty_stmts = 0~1 / 1024

키워드를 균등하게 섞으면 문장을 여는 키워드로 시작하는 경우가 거의
없어서, 파서가 빈 문장 목록을 돌려주고 그 뒤 코드에는 진입조차 하지
못합니다. 즉 panic 불변식은 토크나이저 근처에서만 검증되고 있었습니다.

두 가지를 고쳤습니다.

1. sql_like_fragment의 첫 토큰을 문장을 여는 키워드로 고정 (0~1 -> 10~15)
2. mutated_statement 생성기 추가: 정상 문장에서 출발해 토큰 하나를
   주입합니다. 파서가 문장 종류를 확정한 뒤에 예상치 못한 토큰을 만나므로
   본문 코드에 도달합니다 (nonempty_stmts = ~480 / 1024)

새 테스트가 실제로 무엇을 잡는지 확인했습니다. parse_where에 panic을
심으면 - 유효한 SELECT 안에서만 도달하는 지점입니다:

  기존 테스트 5개: 전부 통과 (놓침)
  신규 테스트 2개: 전부 FAILED (잡음)

cargo test 683 passed / 0 failed (기존 673 + 신규 2, proptest 케이스 증가분 포함).
clippy 경고 70건으로 master와 동일.
f090fb6에서 Cargo.toml의 [[bench]] path를 ./src/benches/... 에서
./benches/... 로 바꿨는데, 의도한 변경이 아니었습니다. 이 PR은 proptest
dev-dependency만 추가하면 됩니다.

그 결과 cargo clippy --all-targets 가 이 브랜치에서만 실패했습니다:

  error: can't find bench `index_benchmark` at path
         /private/tmp/rrdb/benches/index_benchmark.rs

실제 파일은 src/benches/index_benchmark.rs 에 있고, master와 나머지 세
브랜치는 모두 원래 경로를 유지하고 있습니다. 원복 후 --all-targets 가
정상 완료되며 경고 수는 73건으로 master와 동일합니다.
CodeRabbit 리뷰 반영. 기존 property는 전부 '패닉하지 않는다'와
'결정적이다'만 확인하고 결과를 버렸습니다. 지적대로 유효 SQL이 실제로
파싱되는지는 아무도 검증하지 않고 있었습니다.

이게 왜 문제인지 실제로 확인했습니다. parse()가 무조건 빈 결과를
돌려주도록 바꿔봤더니:

  parser_never_panics_on_arbitrary_text     ok
  parser_never_panics_on_sql_like_input     ok
  parser_never_panics_on_mutated_statement  ok
  parsing_is_deterministic                  ok

'아무것도 파싱하지 않는 파서'가 기존 property를 전부 만족합니다.

두 가지를 추가했습니다.

- valid_statement() 생성기 + valid_statements_parse_into_exactly_one_statement
  구성상 유효한 SQL(SELECT/INSERT/UPDATE/DELETE/DROP 7형태, 세미콜론
  유무 포함)이 파싱되고 정확히 한 문장을 만드는지 확인합니다.
- a_bare_statement_keyword_is_an_error
  실패 쪽 계약입니다. 키워드만 있고 뒤가 없는 입력은 조용히 빈 결과가
  아니라 에러여야 합니다.

bite-test: 위의 '빈 결과만 돌려주는 파서'를 넣으면 신규 2건만 FAILED로
뒤집히고 기존 패닉 property는 전부 통과합니다.

cargo test 687 passed / 0 failed (직전 683 + 4).
clippy --all-targets 73건으로 master와 동일.
CodeRabbit 리뷰(#3652563996, #3652563997) 반영.

- arbitrary_sql_text를 proptest::char::any() 기반으로 교체. 기존 ASCII
  정규식은 제어문자(NUL, 단독 \r)와 quote/unbalanced delimiter 경로를
  만들지 못해 패닉 불변성 테스트 범위가 실제 임의 입력보다 좁았음.
  1024 케이스 통과(파서 패닉 없음 유지).

- 결정성 3건(parsing_a_mutated_statement_is_deterministic,
  parsing_is_deterministic, surrounding_whitespace_is_insignificant)이
  Err/Err일 때 ErrorKind 동일성까지 비교. 기존에는 '둘 다 에러'면
  종류가 달라도 통과했고, Ok/Err가 갈리면 조용히 통과했음.
  이제 Ok/Err 엇갈림은 실패로 처리.

cargo test 745 passed / 0 failed, clippy 73건(master와 동일)
@myyrakle
myyrakle force-pushed the test/222-property-based-parser branch from b003aa9 to c76c0b5 Compare August 31, 2026 15:13

@myyrakle myyrakle left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

익힘정도가 별로에요

@myyrakle
myyrakle merged commit 20294da into myyrakle:master Aug 31, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement property-based testing

2 participants