"멈춰버리는 건 빨간불보다 비싸. 빨간불은 최소한 자기 이름은 대거든."
사람이 실제로 돌리는 결과물
단위 테스트 묶음은 테스트 호스트 안의 코드를 증명해. 가족 데스크톱 앱엔 /Applications에 있는 번들을 증명하는 설치 앱 관문이 따로 있어. 이 관문은 open으로 LaunchServices를 거쳐 앱을 띄워. 그래서 앱이 스스로 책임 프로세스가 되고 권한도 자기 것을 써. -ApplePersistenceIgnoreState YES와 따로 정한 결과 파일로 새 인스턴스를 띄우니까, 주인이 저장해 둔 창에 기대지도 않고 덮어쓰지도 않아. 판정은 앱이 자기 로그에 찍은 표시로 해. 그 관문의 첫 검사는 LaunchServices 레슨에서 본 거야. 설치된 실행 파일의 해시가 이 체크아웃의 빌드 산출물과 같아야 해. 설치하고 테스트하는 사이에 다른 체크아웃이 같은 경로의 앱을 바꿔치기할 수 있거든.
open -W는 영원히 기다려
코딩 엔진의 관문은 단계마다 open -n -W -a App --env …를 돌리고 성공 표시를 찾았어. 그런데 한 단계에서 앱이 표시도 안 찍고 종료도 안 하자, 관문은 실패하지 않고 시간 초과도 출력도 없이 그냥 멈춰버렸어. sample로 앱 상태를 떠보니 메인 스레드는 평범한 런 루프에서 멀쩡히 돌고 있었어. 앱은 그냥 할 일이 없었고, 기다리는 쪽이 결함이었던 거지. 그 회귀는 몇 달 동안 안 보였어. 앞 단계가 먼저 실패하고 있었거든. 규칙은 짧아. 모든 대기엔 한도가 있어. 띄우는 명령은 백그라운드로 보내고, 마감을 기준으로 상태를 확인해. 마감이 지나면 띄우는 명령과 그 단계가 띄운 앱 인스턴스만 멈춰. 그러고는 단계 로그를 찍고, 단계 이름과 한도를 밝히면서 실패해. 마감은 일부러 넉넉하게 잡아. 중요한 건 속도가 아니야. 응답을 멈춘 단계가 몇 초 안에 자기 이름을 밝히고 끝난다는 게 핵심이야. 같은 스크립트엔 소리 없이 죽는 경우가 하나 더 있었어. 출력을 파일로 돌린 도우미가 set -e 상태에서 실패하자, 화면엔 아무것도 안 남기고 관문이 끝나버렸어. 그래서 출력을 파일로 돌린 명령이 실패하면, 실패 처리 경로에서 그 파일을 다시 읽어서 보여줘.
사람의 터미널만 돌릴 수 있는 단계
한 단계는 손쉬운 사용 API로 분할 뷰 구분선을 끌었어. 에이전트 셸에서 돌리니 바로 실패했어. 손쉬운 사용 권한은 책임 프로세스 것이고, 에이전트를 띄운 앱엔 그 권한이 없었거든. 다시 시도한다고 해결되지 않아. 그런 단계는 스크립트에 그대로 두되, 권한 없이 돌리면 이유를 밝히며 실패하게 하고 운영자가 직접 돌려야 하는 단계로 보고해. 뒤따르는 단계는 끌기가 만들었을 상태를 미리 만들어두고 증명을 계속 이어가. 초록불을 띄우는 대신 경계를 정직하게 보고하는 거야.
우연히는 통과할 수 없는 증명
데스크톱 클라이언트가 원격 Mac의 파일을 읽는다는 걸 증명하려고, 스모크 테스트는 두 기계에 같은 절대 경로를 만들었어. 내용과 파일 개수는 서로 다르게 해두고 양쪽을 다 확인했지. 셸 쪽은 원격 터미널에 hostname을 쳐보는 걸로 같은 확인을 했고. 로컬 디스크를 읽어서는 절대 통과할 수 없는 테스트였던 거야. iOS 쪽에선 키보드가 한국어로 설정된 시뮬레이터가, 하드웨어 키보드로 친 ASCII를 한글 자모로 바꿔버렸어. 시뮬레이터의 키보드 목록을 바꾸고 앱을 다시 띄우고 나서야 멈췄지.