Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

새 테스트 추가하기

일반적으로 rustc의 버그를 수정하는 모든 PR에는 어떤 형태로든 회귀 테스트가 함께 포함되어야 합니다. 이 테스트는 main에서는 실패하고 PR 적용 후에는 통과해야 합니다. 이러한 테스트는 과거의 실수를 되풀이하지 않도록 막는 데 매우 유용합니다.

가장 먼저 결정해야 할 것은 어떤 종류의 테스트를 추가할지입니다. 이는 변경 사항의 성격과 무엇을 검증하려는지에 따라 달라집니다. 다음은 대략적인 지침입니다.

  • 대부분의 컴파일러 테스트는 compiletest로 수행됩니다.
    • 대부분의 compiletest 테스트는 tests/ui 디렉터리에 있는 UI 테스트입니다.
  • 표준 라이브러리에 대한 변경 사항은 보통 표준 라이브러리 자체 안에서 테스트합니다.
    • 대부분의 표준 라이브러리 테스트는 일반적인 API 동작을 보여 주고 검증하는 doctest로 작성됩니다.
    • 추가 단위 테스트는 library/${crate}/tests에 넣어야 합니다(여기서 ${crate}는 보통 std입니다).
    • alloc 또는 core 크레이트에 대한 테스트는 별도의 크레이트, 즉 각각 alloctests 또는 coretests에 넣어야 합니다.
      • 참고: 불안정 기능에 대한 단위 테스트를 추가할 때는 #![feature(...)] 선언을 library/${crate}tests/lib.rs가 아니라 library/${crate}tests/tests/lib.rs에 추가해야 합니다.
  • 코드가 격리된 시스템의 일부이고 컴파일러 출력을 테스트하는 것이 아니라면 단위 테스트나 통합 테스트를 사용하는 것을 고려하십시오.
  • rustdoc을 실행해야 합니까? rustdoc 또는 rustdoc-ui 테스트를 우선적으로 사용하십시오. 경우에 따라 rustdoc-js도 필요합니다.
  • 그 밖의 compiletest 테스트 스위트는 일반적으로 특수한 목적에 사용됩니다.
    • gdb나 lldb를 실행해야 합니까? debuginfo 테스트 스위트를 사용하십시오.
    • LLVM IR이나 MIR IR을 검사해야 합니까? codegen 또는 mir-opt 테스트 스위트를 사용하십시오.
    • 생성된 바이너리를 어떤 방식으로든 검사해야 합니까? 아니면 다른 모든 테스트 스위트가 목적에 비해 너무 제한적입니까? 그렇다면 run-make를 사용하십시오.
      • 트리 내 rustc와 함께 트리 내 cargo를 검증해야 한다면 run-make-cargo를 사용하십시오.
    • 더 특화된 테스트 스위트에 대해서는 compiletest 장을 확인하십시오.

추가할 테스트의 종류를 결정한 후에는, 다루기 쉽고 오랜 시간이 지나도 유효한 테스트를 작성하는 방법에 대한 지침으로 모범 사례를 참고하십시오(즉, 몇 년 후에 테스트가 실패하거나 수정되어야 할 때, 그 작업을 맡은 사람이 더 쉽게 다룰 수 있도록 하려면 어떻게 해야 하는지에 대한 지침입니다).

UI 테스트 따라 하기

다음은 가장 일반적인 컴파일러 테스트 중 하나인 UI 테스트를 작성하기 위한 기본 안내입니다. 이 튜토리얼에서는 async 오류 메시지에 대한 테스트를 추가합니다.

1단계: 테스트 파일 추가하기

첫 번째 단계는 tests/ui 트리 안의 적당한 위치에 Rust 소스 파일을 만드는 것입니다. 테스트를 만들 때는 좋은 위치와 이름을 정하도록 최선을 다하십시오(자세한 내용은 테스트 구성을 참고하십시오). 이름 짓기는 개발에서 가장 어려운 부분이므로, 여기서부터는 모든 것이 수월할 것입니다!

비동기 테스트를 tests/ui/async-await/await-without-async.rs에 둡시다:

// Provide diagnostics when the user writes `await` in a non-`async` function.
//@ edition:2018

async fn foo() {}

fn bar() {
    foo().await
}

fn main() {}

이 테스트에서 눈여겨볼 몇 가지 사항은 다음과 같습니다:

  • 파일 맨 위는 테스트의 목적을 설명하는 짧은 주석으로 시작해야 합니다.
  • //@ edition:2018 주석은 지시문이라고 하며, 테스트를 빌드하는 방법에 대한 지침을 compiletest에 제공합니다. 여기서는 async가 동작하도록 에디션을 설정해야 합니다(기본값은 2015 에디션입니다).
  • 그다음에는 테스트의 소스가 옵니다. 소스는 간결하고 요점만 담도록 하십시오. 버그 보고서의 예제를 최소화하려는 경우에는 어느 정도 노력이 필요할 수 있습니다.
  • 이 테스트는 빈 fn main 함수로 끝납니다. UI 테스트의 기본 크레이트 타입이 bin인데, 테스트에서 “main not found” 오류가 발생하는 것은 원하지 않기 때문입니다. 또는 #![crate_type="lib"]를 추가할 수도 있습니다.

2단계: 예상 출력 생성하기

다음 단계는 컴파일러로부터 예상 출력 스냅숏을 만드는 것입니다. 이는 --bless 옵션으로 수행할 수 있습니다:

./x test tests/ui/async-await/await-without-async.rs --bless

이렇게 하면 컴파일러를 빌드하고(아직 빌드되지 않은 경우), 테스트를 컴파일한 다음, 컴파일러의 출력을 tests/ui/async-await/await-without-async.stderr라는 파일에 저장합니다.

하지만 이 단계는 실패합니다! 다음과 비슷한 오류 메시지가 표시될 것입니다:

error: /rust/tests/ui/async-await/await-without-async.rs:7: 예상하지 못한 오류: ‘7:10: 7:16: await는 async 함수와 블록 안에서만 허용됩니다 E0728’

이는 stderr에 소스 파일의 오류 주석과 일치하지 않는 오류가 포함되어 있기 때문입니다.

3단계: 오류 주석 추가하기

모든 오류에는 소스 안에 해당 오류의 텍스트를 담은 주석을 달아야 합니다. 이 경우 테스트 파일에 다음 주석을 추가할 수 있습니다:

fn bar() {
    foo().await
    //~^ ERROR `await` is only allowed inside `async` functions and blocks
}

//~^ 물결 캐럿 주석은 해당 오류가 이전 줄에 속한다는 것을 compiletest에 알려 줍니다(자세한 내용은 오류 주석 절을 참고하십시오).

저장한 후 테스트를 다시 실행하십시오:

./x test tests/ui/async-await/await-without-async.rs

이제 통과할 것입니다. 만세!

4단계: 출력 검토하기

이전 단계와 어느 정도 맞물려, 생성된 .stderr 파일을 살펴보고 예상한 모습인지 확인해야 합니다. 새로운 진단 메시지를 추가하는 경우, 특히 Rust를 처음 접하는 사람들에게 메시지가 전반적으로 얼마나 읽기 쉬운지도 함께 고려해 보기에 지금이 좋은 시점입니다.

예제의 tests/ui/async-await/await-without-async.stderr 파일은 다음과 같아야 합니다:

error[E0728]: `await` is only allowed inside `async` functions and blocks
  --> $DIR/await-without-async.rs:7:10
   |
LL | fn bar() {
   |    --- this is not `async`
LL |     foo().await
   |          ^^^^^^ only allowed inside `async` functions and blocks

error: aborting due to previous error

For more information about this error, try `rustc --explain E0728`.

일반적인 컴파일러 출력과 비교하면 몇몇 부분이 조금 다르게 보인다는 것을 알 수 있습니다.

  • $DIR는 시스템마다 달라지는 경로 정보를 제거합니다.
  • LL 값은 줄 번호를 대체합니다. 이렇게 하면 소스 코드가 조금만 바뀌어도 큰 diff가 생기는 일을 막을 수 있습니다. 자세한 내용은 정규화 절을 참고하십시오.

이 단계에서는 테스트를 조정하고, 테스트를 다시 bless하고, 출력을 다시 검토하기 위해 마지막 몇 단계를 여러 번 반복해야 할 수도 있습니다.

5단계: 다른 테스트 확인하기

진단 메시지를 추가하거나 변경하면 테스트 스위트의 다른 테스트에 영향을 주는 경우가 있습니다. PR을 올리기 전 마지막 단계는 다른 부분에 영향을 주지 않았는지 확인하는 것입니다. 보통은 UI 스위트를 실행하는 것부터 시작하는 것이 좋습니다:

./x test tests/ui

다른 테스트가 실패하기 시작하면 무엇이 바뀌었는지, 그리고 새 출력이 타당한지 조사해야 할 수도 있습니다.

--bless 플래그로 출력을 다시 bless해야 할 수도 있습니다.

테스트의 목적을 설명하는 주석

테스트 파일의 첫 번째 주석에서는 테스트의 요점을 요약하고, 그 테스트에서 무엇이 중요한지 강조해야 합니다. 테스트와 관련된 이슈 번호가 있다면 그 이슈 번호를 포함하십시오.

이 주석이 아주 길 필요는 없습니다. 다음과 같은 정도로도 충분할 수 있습니다: “#18060에 대한 회귀 테스트: match 갈래가 잘못된 순서로 매칭되던 문제”.

이러한 주석은 나중에 테스트가 깨졌을 때 무엇이 문제인지 짚어 주는 경우가 많으므로 다른 사람들에게 매우 유용합니다. 또한 어떤 이유로 테스트를 리팩터링해야 할 때도 테스트의 어느 부분이 중요했는지 다른 사람들에게 알려 주므로 유용합니다. 테스트가 원래 검사하려던 것을 더 이상 검사하지 못해서 다시 작성해야 하는 경우가 많은데, 이때 그 테스트가 정확히 무엇을 검사하려고 했던 것인지 알면 도움이 됩니다.