Chrono Mock
JęzykPolski

Dokumentacja wiersza poleceń

Wszystkie flagi obu komend, obie tabele kodów wyjścia i sposób czytania tego, co wraca. Wersja z uzasadnieniem, a nie samymi faktami, stoi na stronie CLI i CI.

Dwie komendy

chrono run uruchamia aplikację w innej dacie i godzinie. chrono calc liczy datę i mówi, co w niej ciekawego. Obie mówią tą samą gramatyką kroków, więc przesunięcie napisane dla jednej znaczy w drugiej to samo.

chrono version odpowiada na trzecie, dużo mniejsze pytanie: która to wersja, którym z dwóch rdzeni jest ten plik i jaką wersję protokołu mówi. Jedna linia na standardowym wyjściu, więc skrypt może ją przechwycić.

chrono license odpowiada na czwarte: na jakiej licencji jest ta wersja, jaka jest klauzula braku gwarancji i jakie komponenty obce są w niej wkompilowane. Mówi też, gdzie na dysku leży pełny tekst licencji i noty komponentów obcych, a gdy uruchomiona kopia ich obok siebie nie ma, przyznaje to wprost. --components wypisuje każdy dołączony komponent wraz z wersją zamiast podsumowania.

chrono presets wypisuje katalog presetów tak, jak czyta go --preset: każdy preset z folderu, zapisany słowami, które czyta gramatyka kalkulatora, oraz każdy plik, który zostałby odrzucony, wraz z powodem. --json zapisuje chronomock.presets/1, a --dir wskazuje inny folder.

chrono run <target> [options]
chrono calc [options]
chrono version        # chrono 0.4.0 (x64, protocol 1)
chrono license        # GPL-3.0-only, and what is bundled
chrono presets        # katalog tak, jak czyta go --preset
chrono --help          # usage for all of them

chrono run - opcje

FlagaCo robi
--at <moment> Moment, od którego zaczyna się sesja, w strefie sesji. Bezwzględny (2038-01-19T03:14:07) albo względny z wiodącym znakiem (+1d, -2h). Jednostki: s m h d w mo q y - m to minuty, a mo miesiące. Bez tej flagi zegar sesji startuje od realnego teraz, więc samo --mode xN po prostu przyspiesza aplikację.
--zone +HH:MM Strefa sesji: strefa, w której zapisany jest moment, i ta, którą raportują wywołania czasu lokalnego w celu. Stały offset, bez zmiany czasu. Bez flagi - strefa hosta.
--mode flow|frozen|xN flow to tempo realne, frozen zatrzymuje zegar, xN przyspiesza go N razy (x60 zamienia godzinę w minutę, x1440 dobę).
--scale-duration Przyspiesz także oś czasu trwania: liczniki taktów, czas przerwań, uśpienia i timery. Czekania na obiekty są liczone i zachowują prawdziwą długość. Domyślnie wyłączone, bo zegar ścienny i stoper to różne pytania. Timer albo opóźnienie nastawione na konkretną datę i godzinę podąża za zegarem sesji z tą flagą i bez niej.
--scale-qpc Skaluje też licznik wysokiej rozdzielczości, z którego czytają czas miniony monotonic w Pythonie 3.13+, Stopwatch w .NET i nanoTime w Javie. Domyślnie wyłączone: aplikacja, która mierzy tym licznikiem własne renderowanie, potrafi wyglądać na zniekształconą.
--ticks N Kończy sesję po N biciach serca, każde po jednej realnej sekundzie, i raportuje. Bez tej flagi narzędzie zostaje przy sesji, dopóki działa cel albo którykolwiek uruchomiony przez niego program na zegarze sesji - launcher, który kończy się od razu, zostawia sesję uruchomionej przez siebie aplikacji.
--timeout <s> Poddaje się po tylu sekundach i kończy kodem 6, dla potoku, który nie może wisieć. Niezależnie od tej flagi rdzeń, który milczy przez 15 sekund, zostaje zatrzymany i też daje kod 6 - w obu przypadkach przebieg został ucięty, więc nie niesie werdyktu.
--args "..." Argumenty dla celu, jako jeden łańcuch, dzielony tą samą regułą, której używa Windows - więc argument ze spacją przeżywa podróż. Dla celu Chromium --user-data-dir i --remote-debugging-port są odmawiane, nie nadpisywane: sesja potrzebuje własnego, odizolowanego profilu, a ciche uruchomienie na twoim prawdziwym profilu przeglądarki to jedyna rzecz, która stać się nie może.
--cwd <katalog> Uruchamia cel w tym katalogu. Bez flagi cel dziedziczy nasz - tak działał każdy przebieg, zanim ta flaga powstała. Pusta wartość to błąd użycia, a nie „brak katalogu": brak i pustka to co innego, a tylko brak jest czymś, co Windows przyjmie. Katalog, którego nie ma, zatrzymuje sesję z kluczem target.cwd_missing jeszcze przed uruchomieniem czegokolwiek, zamiast wyjść później jako cel, który nie chciał wystartować.
--preset <id> Bierze moment i tempo z presets/<id>.json. Wyłączna wobec --at, --mode i --scale-duration, bo preset jest wtedy jedynym źródłem obu.
--param id=value Wypełnia parametr presetu. Powtarzalna, wymaga --preset. Tylko w run data startu wersji próbnej domyślnie bierze się z daty utworzenia pliku celu, więc preset triala liczy od dnia instalacji aplikacji bez wpisywania czegokolwiek.
--set-after T:M Na biciu T zmienia tempo na M. W locie mnożnik wynosi co najmniej 1 - zamrażanie w trakcie sesji nie jest tu oferowane.
--jump-after T:moment Na biciu T przeskakuje zegar na podany moment, bezwzględny albo względny, w strefie sesji.
--force Prowadzi sesję nawet wtedy, gdy werdykt otwarcia mówi, że podmiana nie zadziałała - domyślnie cel jest wtedy zatrzymywany, bo sesja, która niczego nie dowodzi, jest gorsza niż jej brak. Werdykt się nie zmienia, kod wyjścia też nie. Ta flaga rozstrzyga wyłącznie, czy cel biegnie dalej.
--no-embedded Zostawia strony internetowe wewnątrz aplikacji (WebView2, Qt WebEngine) na prawdziwym zegarze i nie otwiera portu diagnostycznego. Bez tej flagi sesja prosi taki silnik o otwarcie lokalnego portu i przez niego przenosi jego strony na zegar sesji - gdy port jest otwarty, każdy inny program na tym komputerze może go użyć, by dotrzeć do tych stron.
--elevated-embedded Dla aplikacji uruchomionej jako administrator, z Chrono Mock uruchomionego również jako administrator. WebView2 pomija dla takiej aplikacji ustawienie, przez które sesja dociera do jej stron, więc sesja zapisuje na czas swojego trwania jedną wartość w rejestrze maszyny (HKLM, nazwaną od pliku .exe aplikacji) i usuwa ją na końcu. Gdy aplikacja działa, każdy inny program na tym komputerze może dotrzeć do jej stron przez port, a aplikacja ma uprawnienia administratora. Nie z --no-embedded.
--keep-background-timers Uruchamia silnik webowy - przeglądarkę aplikacji Chromium lub Electron albo silnik wewnątrz aplikacji - z wyłączonym spowalnianiem timerów w ukrytych oknach, więc zminimalizowane okno zachowuje tempo sesji. Aplikacja pracuje wtedy pełnym tempem także wtedy, gdy jest ukryta. Aplikacja uruchomiona jako administrator przez --elevated-embedded go nie dostaje.
--dry-run Wypisuje, czym byłaby sesja, i nie uruchamia niczego: rozwiązany moment i strefę wraz z ich pochodzeniem, wartości, którymi preset wypełnił swoje parametry, argumenty po podziale oraz to, który z dwóch mechanizmów objąłby cel. Nie powstaje żaden proces ani żaden plik, także ten wskazany przez --report. Kończy 0 dla planu, który da się wykonać, i 2 dla celu, którego nie ma - tym samym kodem, który za ten sam fakt daje prawdziwy przebieg. Plan nie niesie werdyktu i mówi to wprost, bo werdykt ma wyłącznie prawdziwa sesja. Z --json plan jest danymi, w tym session.elevated_embedded i session.elevated_embedded_value, czyli nazwą pliku, po której nazwano by wartość w rejestrze, gdy opcja dotyczy celu .exe, a poza tym null. Nazwa tam nie jest obietnicą zapisu: sesja zapisuje wartość tylko wtedy, gdy sam Chrono Mock działa jako administrator.
--report <plik> Zapisuje czytelny raport do pliku. Werdykt inny niż works prowadzi taki plik banerem o nierzetelnym dowodzie, bo dowód, który przemilcza własną wątpliwość, jest gorszy niż brak dowodu. Ten baner dostaje też sesja z werdyktem works, gdy aplikacja nie przeczytała żadnego zegara, który sesja podmienia, albo gdy jest programem w Go, którego zegara ściennego Chrono Mock nie sięga. Plik nazywa też sam przebieg: wersję, czas startu, cel, jego katalog i flagi.
--report-args Razem z --report wpisuje do tego pliku także argumenty celu, dosłownie. Domyślnie wyłączone, bo argument może zawierać hasło - plik podaje wtedy tylko, ile ich było.
--json Wypisuje sesję jako rekordy maszynowe zamiast raportu - ten sam strumień, który konsumuje okno aplikacji. Wyjście samej aplikacji konsolowej trafia na standardowe wyjście błędów, więc na standardowym wyjściu są tylko rekordy. Aplikacja, która działa jeszcze po końcu sesji, pisze tam dalej, więc to, co czyta wyjście błędów do końca, czeka na nią, i przebieg mówi o tym wprost.

chrono run - kody wyjścia

Kod wyjścia to werdykt sesji, a nie to, co zwróciła aplikacja. Własny kod wyjścia aplikacji jest raportowany osobno, w raporcie i w danych sesji - dlaczego akurat tak, wyjaśnia strona CLI i CI.

KodZnaczenie
0działa - każdy proces, który czytał czas, zobaczył zegar sesji
10działa częściowo - część kanałów objęta, część nie
11nie działa - podmiana nie dosięgła kluczowych kanałów
4nieokreślony - nie dało się ustalić pokrycia. Uczciwe „nie wiem", nigdy udawane „działa"
12cel zniknął zaraz po wstrzyknięciu i nie zostawił niczego na zegarze sesji, co wskazuje na aplikację jednoinstancyjną, która oddała robotę już działającej kopii, albo na przekazanie programowi, do którego sesja nie mogła wejść, zwykle o innej bitowości. Cel, który uruchamia aplikację i się kończy, to launcher - sesja idzie dalej za aplikacją
1błąd użycia - zła flaga, złe wyrażenie albo preset, którego ta komenda nie przyjmuje
2nie udało się uruchomić celu albo wstrzyknąć do niego biblioteki
3błąd wewnętrzny - rdzeń nie wystartował, trafił na nieoczekiwany stan albo zakończył się, zanim zamknął sesję (bez względu na własny kod wyjścia). Taki przebieg nie niesie werdyktu
5operacja niezbudowana w tym wydaniu - na przykład preset, którego moment wymaga kalendarza, a run nie umie go jeszcze wczytać. Celowo odróżnialne od błędu użycia: poprosiłeś o coś, czego jeszcze nie ma, a nie o coś złego
6przebieg został ucięty - minął --timeout albo rdzeń przestał odpowiadać na 15 sekund i został zatrzymany. To nie jest werdykt: sesję przerwano, więc nie dowodzi ona niczego o celu w żadną stronę
7inna sesja już działa - jedna sesja naraz. Wystarczy spróbować ponownie, gdy się zakończy
8sesja doszła do werdyktu, ale jej dowodu nie ma w pliku wskazanym przez --report - dysk był pełny albo inny program trzymał otwarty stary plik, który wtedy zostaje bez zmian. Werdykt nadal stoi w raporcie, który wypisuje polecenie

Jak czytać raport

Bez --json sesja kończy się zwykłym raportem. Każdy wiersz jest albo faktem, który sesja zaobserwowała, albo go nie ma - nic nie jest dopisywane prawdopodobną wartością domyślną.

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 - werdykt rodziny, czyli rodzica i wszystkich procesów potomnych, wraz z ich liczbą. To jest liczba, którą niesie kod wyjścia.
  • exited - własny kod wyjścia aplikacji, i tylko wtedy, gdy zamknęła się sama. Przebieg ograniczony przez --ticks zostawia cel działający, sesję Chromium narzędzie zamyka samo, a sesja, która nigdy nie wystartowała, takiego kodu nie ma - wtedy tego wiersza po prostu nie ma, zamiast zmyślonego zera. Kod awaryjny przychodzi jako duża liczba ujemna, więc dostaje też postać szesnastkową.
  • covered - kanały, które podmiana objęła, z liczbą realnych wywołań każdego, osobno per proces. Liczniki nigdy nie są sumowane między procesami: audyt jednego procesu nie może pożyczać liczb od drugiego. Licznik zatrzymuje się na 1048576 i wtedy pokazuje co najmniej tyle.
  • observed - kanały podpięte, ale celowo zostawione prawdziwe, jak czekania na obiekty czy timery multimedialne. Mają własny kubełek, żeby nikt nie przeczytał ich jako podmienionych.
  • uncovered - kanały, o które aplikacja pytała, a sesja ich nie objęła. To one zamieniają werdykt w częściowy.
  • not fully cleaned up - to, czego sprzątanie nie zdołało usunąć. Przy czystym zakończeniu, czyli normalnie, tego wiersza nie ma.

chrono calc - opcje

Zero języka naturalnego: każda flaga to jeden krok, a kolejność flag to kolejność kroków. Wynik po każdym kroku jest wypisywany, więc zły krok widać, zamiast go zakopywać.

FlagaCo robi
--base today|now|<moment> Punkt wyjścia, domyślnie today. Rozwiązywany w strefie sesji. Niemożliwy dzień, taki jak 2025-02-31, jest odrzucany, a nie po cichu normalizowany na następny miesiąc.
--base-utc <moment> Jak --base, ale początek to chwila w UTC, z opcjonalnym końcowym Z, a nie odczyt zegara ściennego w strefie sesji.
--shift ±N<jednostka> Powtarzalna. Znak obowiązkowy. Jednostki: s m h d w mo q y bd albo ich pełne nazwy. m to minuty, mo miesiące. Miesiące, kwartały i lata składają się na datę cywilną z zaciskiem, więc miesiąc po 31 stycznia to koniec lutego, a nie stała liczba ticków. bd (dni robocze) wymaga --calendar.
--set-time HH:MM:SSUstawia porę dnia na bieżącym wyniku.
--snap <cel> Skok do granicy okresu: som eom soq eoq soy eoy (początek albo koniec miesiąca, kwartału, roku). Początek to pierwszy dzień o 00:00:00, koniec - ostatni o 23:59:59.
--nearest nbd|pbd Najbliższy dzień roboczy naprzód albo wstecz, włącznie z bieżącym, jeśli już nim jest. Wymaga --calendar.
--to-zone +HH:MM Krok: wyraża tę samą chwilę w innym stałym offsecie. Chwila jest zachowana, więc wartość epoch się nie rusza, przesuwają się tylko pola cywilne. Nie mylić z --zone.
--zone +HH:MM Strefa sesji, w której rozwiązywane są today i now i w której renderowany jest wynik. Bez flagi - strefa hosta.
--calendar us-banking|us-federal|pl Wczytuje kalendarz świąt. Dokłada do metadanych dzień roboczy i święto oraz włącza bd i --nearest. Dwa kalendarze amerykańskie różnią się tym, jak obserwują święto wypadające w weekend - czyli dokładnie tym błędem o jeden dzień, który test ma znaleźć. Każdy kalendarz odpowiada za zakres lat: pl od 2002, us-federal od 1960 i us-banking od 2008. Krok w dniach roboczych, który sięga dnia sprzed zakresu, jest odrzucany z kodem 1, a data sprzed zakresu jest pokazana jako nieoceniona. Gdy święta kalendarza sprawdzono z prawem ponad rok temu, późniejszy dzień, który ocenił, dostaje znacznik calendar_outdated, bo może brakować nowszego święta.
--preset <id> --param id=value Nazwany moment z presets/<id>.json, z wypełnionymi parametrami. Wyłączna wobec flag kroków i wobec --analyze, komponuje się z --calendar, --zone i --format.
--analyze <data> Czyta wklejoną datę i mówi, co to jest. Niejednoznaczne 04/08 pokazywane jest w obu odczytach, amerykańskim i polskim, zamiast zgadywane - ta niejednoznaczność to realne źródło błędnych zgłoszeń.
--format <maska> Wypisuje wynik dodatkowo we własnej masce, żeby trafić w format testowanej aplikacji: yyyy-MM-dd HH:mm:ss. Tokeny rozróżniają wielkość liter w sensie .NET i Javy - M to miesiąc, m to minuta.
--json Wyjście maszynowe (chronomock.calc/1) dla każdego z powyższych, więc potok może policzyć datę i podać ją z powrotem do chrono run.
# Last business day of the quarter, American banking calendar
chrono calc --base today --snap eoq --nearest pbd --calendar us-banking

chrono calc - kody wyjścia

Osobna, mniejsza tabela: kalkulator nie ma werdyktu pokrycia, o którym miałby meldować.

KodZnaczenie
0policzono
1błąd użycia - zła flaga, niemożliwa data, przepełnienie zakresu albo plik kalendarza, w którym nie ma ani jednego dnia roboczego
5krok albo jednostka niezbudowane w tym wydaniu, albo krok wymaga kalendarza, którego nie podałeś. Celowo oddzielone od błędu użycia, żeby skrypt odróżnił „jeszcze nie" od „źle"

Dalej