Chrono Mock
언어한국어

명령줄 레퍼런스

두 명령의 모든 플래그, 두 가지 종료 코드 표, 그리고 돌아오는 결과를 읽는 방법입니다. 이유까지 설명한 서술형 버전은 CLI와 CI 페이지에 있습니다.

두 가지 명령

chrono run은 앱을 다른 날짜와 시간으로 시작합니다. chrono calc는 날짜를 계산하고 그 날짜가 왜 흥미로운지 알려 줍니다. 둘은 단계 문법을 공유하므로, 한쪽에서 쓴 이동은 다른 쪽에서도 같은 의미입니다.

chrono version은 세 번째로, 훨씬 작은 질문에 답합니다. 어떤 빌드인지, 실행 파일이 두 코어 중 어느 쪽인지, 어떤 프로토콜 버전을 쓰는지입니다. 표준 출력에 한 줄만 쓰므로 스크립트가 이를 가져갈 수 있습니다.

chrono license는 네 번째에 답합니다. 이 빌드가 배포되는 라이선스, 보증 부인, 링크된 서드파티 구성 요소입니다. 전체 라이선스 문서와 서드파티 고지가 디스크의 어디에 있는지도 알려 주며, 실행 중인 사본 옆에 이 파일들이 없으면 그 사실을 인정합니다. --components를 붙이면 요약 대신 포함된 모든 구성 요소를 버전과 함께 나열합니다.

chrono presets는 --preset이 읽는 방식 그대로 시나리오 카탈로그를 나열합니다. 폴더의 모든 시나리오를 계산기 문법이 읽는 말로 보여 주고, 거부할 파일이 있으면 그 이유와 함께 보여 줍니다. --json은 chronomock.presets/1을 출력하고 --dir은 다른 폴더를 지정합니다.

chrono run <target> [options]
chrono calc [options]
chrono version        # chrono 0.4.0 (x64, protocol 1)
chrono license        # GPL-3.0-only와 포함된 구성 요소
chrono presets        # --preset이 읽는 방식의 카탈로그
chrono --help          # 모든 명령의 사용법

chrono run - 옵션

플래그동작
--at <moment> 시작할 시점으로, 세션 시간대 기준입니다. 절대 시점(2038-01-19T03:14:07) 또는 부호로 시작하는 상대 시점(+1d, -2h)입니다. 단위는 s m h d w mo q y이며, m은 분, mo는 월입니다. 이 플래그가 없으면 세션 시계는 실제 현재 시각에서 시작하므로, --mode xN만 쓰면 대상을 단순히 더 빠르게 실행합니다.
--zone +HH:MM 세션 시간대입니다. 시점이 표기되는 시간대이자 대상의 로컬 시간 호출이 보고하는 시간대입니다. 서머타임이 없는 고정 오프셋입니다. 지정하지 않으면 호스트 자체의 시간대입니다.
--mode flow|frozen|xN flow는 실제 속도로, frozen은 시계를 멈춘 채로, xN은 N배 빠르게 실행합니다(x60이면 한 시간이 1분, x1440이면 하루가 1분이 됩니다).
--scale-duration 경과 시간 축도 빠르게 합니다. 틱 카운트, 인터럽트 시간, 슬립, 타이머가 대상입니다. 객체 대기는 횟수만 세고 실제 길이를 유지합니다. 벽시계와 스톱워치는 서로 다른 질문이므로 기본값은 꺼짐입니다. 날짜와 시간을 지정한 타이머나 지연은 이 옵션이 있든 없든 세션 시계를 따릅니다.
--scale-qpc 고해상도 카운터도 함께 배율을 적용합니다. Python 3.13+의 monotonic, .NET의 Stopwatch, Java의 nanoTime이 경과 시간을 여기서 읽습니다. 기본값은 꺼짐입니다. 이 카운터로 렌더링 시간을 재는 앱은 왜곡되어 보일 수 있기 때문입니다.
--ticks N N번의 하트비트(각각 실제 1초) 뒤에 세션을 끝내고 보고합니다. 이 옵션이 없으면 도구는 대상과, 대상이 시작한 세션 시계 위에서 실행되는 모든 프로그램이 끝날 때까지 연결을 유지합니다. 곧바로 종료되는 런처는 자신이 시작한 애플리케이션에 세션을 넘깁니다.
--timeout <s> 지정한 초가 지나면 포기하고 종료 코드 6으로 끝납니다. 멈춰서는 안 되는 파이프라인용입니다. 이 플래그와 별개로, 15초 동안 아무 응답이 없는 코어는 중지되며 마찬가지로 6으로 끝납니다. 두 경우 모두 실행이 중간에 끊긴 것이므로 판정이 없습니다.
--args "..." 대상에 전달할 인수를 하나의 문자열로 지정하며, Windows와 같은 규칙으로 분리하므로 공백이 포함된 인수도 유지됩니다. Chromium 대상에서는 --user-data-dir과 --remote-debugging-port가 덮어쓰기 대신 거부됩니다. 세션에는 자체 격리 프로필이 필요하며, 조용히 실제 브라우저 프로필에서 실행되는 일은 절대 일어나서는 안 되기 때문입니다.
--cwd <dir> 대상을 해당 디렉터리에서 시작합니다. 지정하지 않으면 이 플래그가 생기기 전의 모든 실행처럼 대상이 이쪽 디렉터리를 상속합니다. 빈 값은 “디렉터리 없음”이 아니라 사용 오류입니다. 지정하지 않음과 비어 있음은 다른 것이며, Windows에 줄 수 있는 것은 지정하지 않음뿐이기 때문입니다. 존재하지 않는 디렉터리는 무엇이든 실행하기 전에 target.cwd_missing으로 세션을 중단시킵니다. 그렇지 않으면 나중에 시작되지 않는 대상으로 드러나게 됩니다.
--preset <id> 시점과 속도를 presets/<id>.json에서 가져옵니다. 이때 시나리오가 둘 모두의 유일한 출처가 되므로 --at, --mode, --scale-duration과 함께 쓸 수 없습니다.
--param id=value 시나리오 매개변수를 채웁니다. 여러 번 지정할 수 있고 --preset이 필요합니다. run에서만 체험 시작일의 기본값이 대상 실행 파일 자체의 생성일이므로, 체험 시나리오는 직접 입력하지 않아도 앱을 설치한 날부터 셉니다.
--set-after T:M T번째 하트비트에서 속도를 M으로 바꿉니다. 실행 중에는 배율이 1 이상이며, 세션 도중의 정지는 여기서 제공하지 않습니다.
--jump-after T:moment T번째 하트비트에서 시계를 해당 시점(절대 또는 상대, 세션 시간대 기준)으로 점프시킵니다.
--force 시작 시점의 판정이 시간 대체가 적용되지 않았다고 해도 세션을 유지합니다. 기본적으로는 대신 대상이 중지되는데, 아무것도 증명하지 못한 세션은 세션이 없는 것보다 나쁘기 때문입니다. 판정은 바뀌지 않으며 종료 코드도 마찬가지입니다. 이 플래그는 대상이 계속 실행될지만 결정합니다.
--no-embedded 애플리케이션 안의 웹 페이지(WebView2, Qt WebEngine)를 실제 시계에 그대로 두고 디버깅 포트를 열지 않습니다. 이 옵션이 없으면 세션은 이런 엔진에 로컬 포트를 열도록 요청하고 그 포트를 통해 해당 페이지를 세션 시계에 올립니다. 포트가 열려 있는 동안에는 이 컴퓨터의 다른 어떤 프로그램도 그 포트로 해당 페이지에 접근할 수 있습니다.
--elevated-embedded 관리자 권한으로 실행되는 애플리케이션을 위한 옵션이며, 마찬가지로 관리자 권한으로 실행 중인 Chrono Mock에서 사용합니다. 이런 애플리케이션에서는 WebView2가 세션이 페이지에 접근하는 데 쓰는 설정을 무시하므로, 세션은 지속되는 동안 컴퓨터 레지스트리(HKLM, 애플리케이션의 .exe 이름으로)에 값 하나를 기록하고 끝날 때 삭제합니다. 애플리케이션이 실행되는 동안에는 이 컴퓨터의 다른 어떤 프로그램도 그 포트로 해당 페이지에 접근할 수 있고, 애플리케이션은 관리자 권한을 갖습니다. --no-embedded와 함께 쓸 수 없습니다.
--keep-background-timers 웹 엔진(Chromium 또는 Electron 애플리케이션의 브라우저, 또는 애플리케이션 안의 엔진)을 숨겨진 창의 타이머 감속을 끈 채로 시작하므로, 최소화된 창도 세션 속도를 유지합니다. 그러면 애플리케이션도 숨겨져 있는 동안 전속력으로 동작합니다. --elevated-embedded로 관리자 권한으로 시작한 애플리케이션에는 적용되지 않습니다.
--dry-run 세션이 어떻게 될지 출력하고 아무것도 시작하지 않습니다. 확정된 시점과 시간대 및 그 출처, 시나리오가 각 매개변수를 무엇으로 채웠는지, 분리된 인수, 대상이 두 메커니즘 중 어느 쪽을 쓸지를 보여 줍니다. 프로세스를 시작하지 않고, --report가 지정한 파일을 포함해 어떤 파일도 쓰지 않습니다. 실행 가능한 계획이면 0으로, 대상이 없으면 2로 종료하며, 이는 실제 실행이 같은 사실에 대해 내는 코드와 같습니다. 계획에는 판정이 없고 그 사실을 밝힙니다. 판정이 있는 것은 실제 세션뿐입니다. --json을 붙이면 계획이 데이터로 출력되며 session.elevated_embedded와 session.elevated_embedded_value가 포함됩니다. 후자는 옵션이 .exe 대상에 적용될 때 레지스트리 값의 이름이 될 파일 이름이고, 그렇지 않으면 null입니다. 이름이 있다고 해서 기록이 보장되는 것은 아니며, 세션은 Chrono Mock 자체가 관리자 권한으로 실행 중일 때만 값을 기록합니다.
--report <path> 사람이 읽는 보고서를 파일에 기록합니다. 판정이 동작함이 아니면 파일 맨 앞에 신뢰할 수 없는 증거라는 배너가 붙습니다. 자신의 의심을 숨기는 증거는 없는 것보다 나쁘기 때문입니다. 판정이 동작함인 세션도, 세션이 바꾸는 시계를 애플리케이션이 하나도 읽지 않았거나, 애플리케이션이 Go 프로그램이어서 Chrono Mock이 그 벽시계에 닿지 못하는 경우에는 이 배너가 붙습니다. 파일에는 실행 자체도 기록됩니다. 버전, 시작 시각, 대상, 그 폴더, 플래그입니다.
--report-args --report와 함께 쓰면 대상의 인수도 그 파일에 그대로 기록합니다. 기본값은 꺼짐입니다. 인수에 비밀번호가 들어 있을 수 있기 때문이며, 이때 파일에는 인수의 개수만 적힙니다.
--json 보고서 대신 세션을 기계가 읽을 수 있는 레코드로 출력합니다. 창 자체가 소비하는 것과 같은 스트림입니다. 콘솔 애플리케이션 자신의 출력은 표준 오류로 가므로, 표준 출력에는 레코드만 담깁니다. 세션이 끝날 때 아직 실행 중인 애플리케이션은 계속 그곳에 쓰므로, 표준 오류를 끝까지 읽는 쪽은 그것을 기다리게 되며 실행 결과에 그 사실이 표시됩니다.

chrono run - 종료 코드

종료 코드는 앱이 반환한 값이 아니라 세션의 판정입니다. 앱 자체의 종료 코드는 보고서와 세션 데이터에 따로 보고됩니다. 왜 이렇게 되어 있는지는 CLI와 CI 페이지에서 설명합니다.

코드의미
0동작함 - 시간을 읽은 모든 프로세스가 세션 시계를 보았음
10일부 동작함 - 일부 채널은 커버되고 일부는 커버되지 않음
11동작하지 않음 - 대체가 핵심 채널에 도달하지 못함
4판단 불가 - 커버리지를 확인할 수 없음. 솔직한 “알 수 없음”이며, 통과한 척하는 일은 절대 없습니다
12대상이 인젝션 직후 사라졌고 세션 시계 위에서 실행되는 것이 아무것도 남지 않았음. 단일 인스턴스 애플리케이션이 이미 실행 중인 사본에 넘겼거나, 세션이 들어갈 수 없는 프로그램(보통 비트 수가 다른 것)에 넘겼음을 시사합니다. 애플리케이션을 시작하고 끝나는 대상은 런처이며, 세션은 그 애플리케이션을 위해 계속됩니다
1사용 오류 - 잘못된 플래그, 잘못된 식, 또는 이 명령이 받아들이지 않는 시나리오
2대상을 실행하거나 인젝션할 수 없었음
3내부 오류 - 엔진을 시작할 수 없었거나, 예기치 않은 상태를 만났거나, 세션을 닫기 전에 멈춤(엔진 자신의 종료 코드와 무관하게). 이런 실행에는 판정이 없습니다
5이 릴리스에는 해당 작업이 빌드되어 있지 않음 - 예를 들어 시점에 달력이 필요하지만 run이 아직 달력을 불러올 수 없는 시나리오. 사용 오류와 의도적으로 구분했습니다. 잘못된 것이 아니라 아직 존재하지 않는 것을 요청한 것이기 때문입니다
6실행이 중간에 끊김 - --timeout이 만료되었거나, 엔진이 15초 동안 응답하지 않아 중지됨. 이것은 판정이 아닙니다. 세션이 중단되었으므로 대상에 대해 어느 쪽으로도 아무것도 증명하지 못합니다
7다른 세션이 이미 실행 중임 - 세션은 한 번에 하나뿐입니다. 끝난 뒤 다시 시도하면 됩니다
8세션이 판정에 도달했지만 그 증거가 --report로 지정한 파일에 없음 - 디스크가 가득 찼거나 다른 프로그램이 이전 파일을 열어 두어, 그 파일은 그대로 남습니다. 판정은 명령이 출력하는 보고서에 그대로 있습니다

보고서 읽는 법

--json이 없으면 세션은 일반 보고서로 끝납니다. 모든 줄은 세션이 관찰한 사실이거나 아예 없습니다. 그럴듯한 기본값으로 채워지는 것은 아무것도 없습니다.

Chrono Mock - session report
  target:   .\build\test.exe
  verdict:  WORKS - time substitution took effect  (processes: 1)
            every process that read time saw the session clock
  session:  fake clock reached 2038-01-19T03:14:08
            real elapsed 1.3s, fake elapsed 1.3s
  exited:   the target closed itself with code 3
  covered channels (substituted, with call counts, 10 calls in all):
            - pid 21240: GetSystemTimeAsFileTime (5 calls)
            - pid 21240: GetLocalTime (5 calls)
  observed channels (hooked but left real):
            - pid 21240: NtCreateUserProcess (0 calls)
  warnings:
            - the target opened a network connection ...
  • verdict - 부모와 모든 자식 프로세스를 아우르는 패밀리 판정과 프로세스 수입니다. 종료 코드가 담는 것이 이 값입니다.
  • exited - 앱 자체의 종료 코드이며, 스스로 종료한 경우에만 나옵니다. --ticks로 제한한 실행은 대상을 실행 중인 채로 두고, Chromium 세션은 도구가 직접 닫으며, 시작하지 않은 세션에는 이런 코드가 없습니다. 이 경우 지어낸 0을 보여 주는 대신 이 줄이 없습니다. 충돌 코드는 큰 음수로 오므로 16진수 형태도 함께 표시됩니다.
  • covered - 대체가 도달한 채널과, 프로세스별로 각각 실제로 몇 번 호출되었는지입니다. 호출 횟수는 프로세스 간에 합산하지 않습니다. 한 프로세스의 감사가 다른 프로세스의 숫자를 빌려 와서는 안 되기 때문입니다. 횟수는 1048576에서 멈추고 그 뒤로는 최소 그 횟수로 표시됩니다.
  • observed - 훅은 걸었지만 일부러 실제 값 그대로 둔 채널입니다. 객체 대기와 멀티미디어 타이머 같은 것이 해당합니다. 별도의 분류가 있어서 대체된 것으로 읽히는 일이 없습니다.
  • uncovered - 앱이 요청했지만 세션이 커버하지 못한 채널입니다. 판정을 일부로 바꾸는 것이 이것입니다.
  • not fully cleaned up - 종료 처리에서 제거하지 못한 모든 것입니다. 정상적으로 끝난 경우, 즉 일반적인 경우에는 나타나지 않습니다.

chrono calc - 옵션

자연어는 없습니다. 각 플래그가 하나의 단계이고, 플래그의 순서가 곧 단계의 순서입니다. 각 단계 뒤의 결과가 출력되므로 잘못된 단계는 묻히지 않고 눈에 보입니다.

플래그동작
--base today|now|<moment> 어디서 시작할지로, 기본값은 today입니다. 세션 시간대에서 해석됩니다. 2025-02-31 같은 불가능한 날은 거부되며, 조용히 다음 달로 정규화되는 일은 없습니다.
--base-utc <moment> --base와 같지만, 시작점은 세션 시간대의 벽시계 값이 아니라 UTC의 한 시점이며 끝에 Z를 붙여도 됩니다.
--shift ±N<unit> 여러 번 지정할 수 있습니다. 부호는 필수입니다. 단위는 s m h d w mo q y bd 또는 전체 이름입니다. m은 분, mo는 월입니다. 월, 분기, 연은 달력 날짜에 맞춰 범위를 제한하며 적용되므로, 1월 31일의 한 달 뒤는 고정된 틱 수가 아니라 2월 말일입니다. bd(영업일)에는 --calendar가 필요합니다.
--set-time HH:MM:SS현재 결과의 시각을 설정합니다.
--snap <target> 기간의 경계로 점프합니다. som eom soq eoq soy eoy(월, 분기, 연의 시작 또는 끝)입니다. 시작은 첫날 00:00:00, 끝은 마지막 날 23:59:59입니다.
--nearest nbd|pbd 앞쪽 또는 뒤쪽으로 가장 가까운 영업일이며, 현재 날이 이미 영업일이면 그 날을 포함합니다. --calendar가 필요합니다.
--to-zone +HH:MM 단계입니다. 같은 순간을 다른 고정 오프셋으로 다시 표현합니다. 순간은 보존되므로 에포크 값은 변하지 않고 민간 필드만 바뀝니다. --zone과 혼동하지 마세요.
--zone +HH:MM 세션 시간대로, today와 now를 해석하고 결과를 표시하는 데 쓰입니다. 지정하지 않으면 호스트의 시간대입니다.
--calendar us-banking|us-federal|pl 공휴일 달력을 불러옵니다. 메타데이터에 영업일과 공휴일이 추가되고 bd와 --nearest가 활성화됩니다. 두 미국 달력은 주말에 걸린 공휴일을 처리하는 방식이 다르며, 이는 테스트가 찾아내야 하는 바로 그런 하루 차이의 오류입니다. 각 달력은 일정 범위의 연도에 대해서만 답합니다. pl은 2002년부터, us-federal은 1960년부터, us-banking은 2008년부터입니다. 영업일 단계가 그 범위보다 앞선 날에 닿으면 종료 코드 1로 거부되고, 범위보다 앞선 날짜는 판단하지 않은 것으로 표시됩니다. 달력의 공휴일을 법령과 마지막으로 대조한 지 1년이 넘었다면, 그 달력이 판단한 이후의 날에는 calendar_outdated 표시가 붙습니다. 더 새로운 공휴일이 빠져 있을 수 있기 때문입니다.
--preset <id> --param id=value presets/<id>.json의 이름이 붙은 시점이며 매개변수가 채워집니다. 단계 플래그 및 --analyze와는 함께 쓸 수 없고, --calendar, --zone, --format과는 조합할 수 있습니다.
--analyze <date> 붙여 넣은 날짜를 거꾸로 읽어 무엇인지 알려 줍니다. 모호한 04/08은 추측하지 않고 미국식과 폴란드식 두 가지 해석을 모두 보여 줍니다. 이 모호함이 잘못된 버그 보고의 실제 원인이기 때문입니다.
--format <mask> 테스트 대상 앱의 형식과 맞도록 결과를 사용자 마스크로도 출력합니다. 예: yyyy-MM-dd HH:mm:ss. 토큰은 .NET과 Java의 의미로 대소문자를 구분합니다. M은 월, m은 분입니다.
--json 위의 모든 경우에 대한 기계용 출력(chronomock.calc/1)으로, 파이프라인이 날짜를 계산해 곧바로 chrono run에 다시 넘길 수 있습니다.
# 분기의 마지막 영업일, 미국 은행 달력
chrono calc --base today --snap eoq --nearest pbd --calendar us-banking

chrono calc - 종료 코드

별도의 더 작은 표입니다. 계산기에는 보고할 커버리지 판정이 없습니다.

코드의미
0계산됨
1사용 오류 - 잘못된 플래그, 불가능한 날짜, 범위 초과, 또는 영업일이 하나도 없는 달력 파일
5해당 단계나 단위가 이 릴리스에 빌드되어 있지 않거나, 단계에 필요한 달력을 지정하지 않았음. 스크립트가 “아직”과 “잘못됨”을 구분할 수 있도록 일부러 사용 오류와 분리했습니다

다음으로