Napisanie testu pod nieistniejący constant

1

Słowo wstępu - to nie jest problem

  • Ponieważ PHP jest zwalony, to da się definiować stałe globalne w runtimie. Również stałe które są używane w wewnętrznych algorytmach.
  • W PHP jest też konwencja że sporo feature'ów ma flagi, więc często jak się chce sprawdzić czy jakiś extension lub feature języka już jest, to się sprawdza function_exists('mb_strlen') albo defined(PREG_UNATMCHED_AS_NULL). Jeśli constant istnieje - to można użyć feature'a.

Mój use-case - to nie jest problem

Piszę bibliotekę która ma wspierać PHP 7.1, 7.2, 7.3, 7.4 oraz 8.0 i 8.1. Tak, wiem że 7.3 i starsze są out of life - w projektach ich nie używam, ale to jest biblioteka więc wspieram.

W PHP 7.3 doszedł constant PREG_UNAMATCHED_AS_NULL, który zwiększa trochę wydajność. To znaczy że biblioteka na nowszych wersjach PHP może dodać jakieś ulepszenie, a na starszych po prostu nie.

Kod działa tak:

function moja_funkcja() {
  if (defined('PREG_UNMATCHED_AS_NULL')) { // const jest, jesteśmy na PHP 7.3+
    return preg_match($p, $s, PREG_UNMATCHED_AS_NULL | PREG_CAPTURE_OFFSET); // optymalizacja
  }
  return preg_match($p, $s, PREG_CAPTURE_OFFSET);  // stasza implementacja
}

Stałe PREG_UNMATCHED_AS_NULL oraz PREG_CAPTURE_OFFSET są systemowe, z modułu PCRE. Jedna z nich jest od początku, druga była dodana w PHP 7.3

Atak "constant override" - to nie jest problem

Tylko że w PHP da się dodać constant, jeśli nie istnieje! :O

function hakuj() {
  define(PREG_UNMATCHED_AS_NULL, 2);
}

hakuj();

function moja_funkcja() {
  if (defined('PREG_UNMATCHED_AS_NULL')) { // const jest, nawet na starszych PHP teraz
    return preg_match($p, $s, PREG_UNMATCHED_AS_NULL | PREG_CAPTURE_OFFSET); // optymalizacja
  }
  return preg_match($p, $s, PREG_CAPTURE_OFFSET);  // stasza implementacja
}

A to znaczy, że jeśli ktoś używa tej biblioteki na starszej wersji PHP, np 7.1 albo 7.2, na której nie ma ani funkcjonalności ani flagi PREG_UNMATCHED_AS_NULL, to on jest w stanie na takim systemie dodać ten const PREG_UNMATCHED_AS_NULL. PHP o nim nie wie. I wtedy, if if (defined('PREG_UNMATCHED_AS_NULL')), i zostanie wywołany kawałek kodu z optymalizacją, mimo że nie powinien.

Ergo, nie mogę użyć if (defined('PREG_UNMATCHED_AS_NULL')) do sprawdzenia czy feature istnieje, bo można nadpisać po prostu const. Muszę użyć innego rozwiązania, np takiego:

function moja_funkcja() {
  if (version_compare(PHP_VERSION, '7.3.0', '>=')) { // na pewno jesteśmy na PHP 7.3+
    return preg_match($p, $s, PREG_UNMATCHED_AS_NULL | PREG_CAPTURE_OFFSET); // optymalizacja
  }
  return preg_match($p, $s, PREG_CAPTURE_OFFSET);  // stasza implementacja
}

Wtedy hacker może sobie nadpisywać consty do woli. Super. Pierwszy problem rozwiązany.

Teraz jak to przetestować? - to nie jest problem

No oczywiście, bardzo prosto. Napisać test który deklaruje nową stałą, i sprawdza czy leci warning z użya biblioteki

public function constantOverrideAttack() {
  // given
  define('PREG_UNMATCHED_AS_NULL', 5); // na PHP 7.1 lub 7.2 to doda nieistniejący const

  // when
  moja_funkcja(); // tutaj leci warning, bo const jest, ale funkcjonalności nie ma
}

I to jest spoko test.

No więc do jasnej anielki co ten TomRiddle chce - TO JEST PROBLEM! TO CHCĘ NAPRAWIĆ

Ano problem jest taki, że tego consta z tego testu się nie da usunąć po teście. A to znaczy że inne testy, które się wykonają po nim też go będą miały, a to znaczy że testy nie są odseparowane od siebie, a muszą być.

Pomysły jakie mam.

  1. Zhackować PhpUnit żeby odpalał ten konkretny test jako ostatni. Zadziała tak długo jak będzie jeden, bo jak będę chciał więcej to klops.
  2. Odpalać dwa testy w osobnych procesach, tak żeby nie było zdefiniowanej tej stałej, ale to spowolni testy znacznie z 1ms do jakichś 2-3 sekund.
  3. Użyć extensiona "runkit" który może usunąć stałą, ale to ma szereg wad: po pierwsze nie da się łatwo zainstalować runkit na Github Actions, ale to bym ogarnął; ciężko jest zainstalować runkit zarówno na windowsie jak i na linuxie bo to nie jest standardowy extension, i trzeba go specjalnie budować ze źródeł. Dużo zachodu. Jak kontrybutor ściągnie to prędzej się pochlasta niż sobie zbuduje ten extension (bo on np potrzebuje Visual studio redistributables co ma 6gb i nie instaluje się go wcale tak prosto).

Byłoby spoko gdyby dało się zredefiniować stałą, wtedy ustawiłbym ją z powrotem na null i inne testy byłyby safe. Ale niestety nie da się, pierwsza wartość zostaje.

Najbardziej się skłaniam ku opcji 2, ale to jest wolne strasznie.

O czym myślałem i czego nie zrobię - to nie jest problem

  • Nie użyję żadnych linterów/checkerów/stylechecków do wykrywania czy użyłem defined('PREG_UNMATCHED_AS_NULL'), bo to mniej rzetelne rozwiązanie niż testy.
  • Nie wydzielę kawałka kodu do serwisu który potem będzie zmockowany, bo tak czy tak jego też musiałbym przetestować.
1

Zrób inną stałą, przypisz do niej wartość tej produkcyjnej stałej, odpal test, sprawdź warning i wartość stałej.

0
Afish napisał(a):

Nie sprawdzisz, czy jest używana, bo równie dobrze może to być zbieżność wartości.

Sprawdzę, bo jeśli odpalę to na starym PHP, w którym nie ma argumentu i nie ma też stałej, ale ją zdefiniuje to poleci warning. Ten test co napisałem ze zdefiniowanej zmiennej działa i przechodzi jak nie używam jej, i failuje jak używam, tak jak powinien.

Problem jest tylko taki że stała zostaje. Ten Twój pomysł z inną stałą nic nie da, bo wtedy test zawsze by przeszł, ergo byłby worthless. Nie wiem skąd Ci w ogóle taki pomysł przyszedł do głowy, czemu inna zmienna miałby cokolwiek zmienić? ;|

2

Po pierwsze to IMHO coś źle kombinujesz jak potrzebujesz mieć w kodzie tą samą stałą co systemowa. Jeśli chodzi Ci o to, że stała była w danej wersji PHPa to sprawdzasz wersję i w teście też możesz pokusić się o sprawdzenie wersji PHPa zamiast sprawdzać jakieś podatności PHPa na definiowanie zdefiniowanych stałych. Możesz też pobrać przecież wszystkie stałe przez get_defined_constants() i sprawdzasz w teście czy ona już istnieje.
Jeśli istnieje to test rzuca błąd lub nie.
Nie jestem specem od TDD więc dalej pewnie wchodzić w dyskusje nie będę.

0

A po co Ci takie testy? Wygląda jakbyś chciał testować zachowanie zewnętrznych komponentów.

Możliwe, że architektura Twojego rozwiązania ma zbyt sztywne zależności, np. coś w stylu : Moduł -> Zewnętrzna biblioteka

Gdybyś miał odcięcie typu: Moduł -> Dostawca Funkcjonalności <- Konkretny dostawca -> Zewnętrzna biblioteka, to Twoje testy Modułu byłyby niezależne od tej zewnętrznej biblioteki.
A tak testujesz czy zewnętrzna biblioteka nie została zhackowana().

0

@jurek1980 oraz @yarel zrozumiałem że nie napisałem zrozumiale tematu. Przepisałem cały pierwszy post od nowa, przeczytajcie proszę go jeszcze raz.

2

W twoim kodzie:

Dodać do static code analysis error gdy ktoś robi define(PREG_UNMATCHED_AS_NULL*

W kodzie usera:

Jeżeli ktoś psuje stałe zmienne globalne systemowe, to niech się nie dziwi że coś nie działa

@TomRiddle

Żebym polegał na IDE zamiast na testach jednostkowych? Super, super pomysł.

Statyczną analizę kodu możesz odpalać nie tylko z IDE, więc słaby point.

Możesz, a właściwie powinieneś do CI/CD podpiąć.

BTW: Czy statyczna analiza kodu to nie jest jakby test? dodatkowo możesz napisać test który wykonuje statyczną analizę kodu i bada output ;)

Czyli mam zostawić specjalnie dziurę w bibliotece? Kolejny super pomysł.

Jeżeli ktoś będzie chciał, to i tak ci rozwali bibliotekę jakąś refleksją czy czymś (nie znam PHP)

A to ci nie pomoże? kolejny ifek

https://www.php.net/manual/en/function.phpversion.php

<?php
// prints e.g. 'Current PHP version: 4.1.1'
echo 'Current PHP version: ' . phpversion();

// prints e.g. '2.0' or nothing if the extension isn't enabled
echo phpversion('tidy');
?>

@TomRiddle:

no dobra, a tak na serio to czemu dodanie sprawdzania wersji języka jest nie-nie? byś sprawdzał czy dany ""feature"" jest, a w dodatku czy ktoś czegoś nie zepsuł na starszej wersji

@TomRiddle:

To napisz warstwę abstrakcji, jakiś LanguageFeaturesAndVersionProvider

który Ci powie jakie rzeczy w języku są dostępne i jaką ma wersję, a w testach będziesz mógł wstawiać tam różne rzeczy i testować czy działa

4

Dobrze rozumiem i dobrze zrozumiałem wcześniej.
Masz źle napisaną funkcjonalność która nie sprawdza wersji PHPa i potem chcesz to testować.
IMHO źle napisałeś, to żaden test logiczny pod to nie podejdzie.
Obecnie sprawdzasz czy stała istnieje. Po co? Załóżmy, że ktoś omyłkowo zdefiniuje stałą o tej samej nazwie z wartością 5, co w PCRE może być zupełnie innym parametrem. Ty w kodzie sprawdzasz czy stała jest a nie jej wartość.
Tak nie ma co ani pisać kodu ani testować tego. Zaraz będziesz testował czy test który napisałeś się uruchomi bo jak się nie uruchomi to nie przetestuje pierwotnej funkcjonalności.

0
jurek1980 napisał(a):

Dobrze rozumiem i dobrze zrozumiałem wcześniej.
Masz źle napisaną funkcjonalność która nie sprawdza wersji PHPa i potem chcesz to testować.

Mam dobrze napisaną funkcjonalność teraz, i chcę napisać czy na pewno jest dobra.

jurek1980 napisał(a):

IMHO źle napisałeś, to żaden test logiczny pod to nie podejdzie.
Obecnie sprawdzasz czy stała istnieje. Po co? Załóżmy, że ktoś omyłkowo zdefiniuje stałą o tej samej nazwie z wartością 5, co w PCRE może być zupełnie innym parametrem. Ty w kodzie sprawdzasz czy stała jest a nie jej wartość.

No właśnie nie chce sprawdzać czy stała istnieje. Dlatego chce napisać test pod to. Jeśli kod sprawdza czy stała istnieje, test ma się wywalić. Jeśli test nie sprawdza czy stała istnieje, to test ma przejść.

Tak nie ma co ani pisać kodu ani testować tego. Zaraz będziesz testował czy test który napisałeś się uruchomi bo jak się nie uruchomi to nie przetestuje pierwotnej funkcjonalności.

Jest. Kod ma nie używać stałej, bo się naraża na "constant override". Jeśli kod używa stałej, mój test ma się wyawlić, ten test:

public function constantOverrideAttack() {
  // given
  define('PREG_UNMATCHED_AS_NULL', 5); // na PHP 7.1 lub 7.2 to doda nieistniejący const

  // when
  moja_funkcja(); // tutaj leci warning, bo const jest, ale funkcjonalności nie ma
}

Jego wadą jest to, że zostawia stałą w procesie testu.


Podsumuję to jeszcze raz: kod działa spoko. Ja chcę teraz napisać test. Tylko w teście chce wprowadzić zmiany - znaleźć sposób na dodanie tego consta, tak żeby nie wpłynął na inne testy.

1

Ja bym wrzucił if na wersję PHPa, napisał dwa testy, osobno na starego oraz nowego PHPa, i zwyczajnie odpalał testy na obydwóch wersjach (z wykorzystaniem Nixa / Dockera coś takiego powinno dać się klepnąć w godzinę or so).

Jeśli chcesz mieć 100% pewność, że Twój kod działa na starszym PHPie, to siłą rzeczy musisz uruchomić testy na starszej wersji PHPa - symulacja istnienia/nieistnienia jakiejś globalnej stałej żadnej gwarancji nie daje, bo równie dobrze dokumentacja może być dziurawa, stała mogła być wprowadzona w jakiejś patch-wersji, mogłeś źle skopiować/przepisać jej nazwę itd.

0
Patryk27 napisał(a):

Ja bym wrzucił if na wersję PHPa, napisał dwa testy, osobno na starego oraz nowego PHPa, i zwyczajnie odpalał testy na obydwóch wersjach (z wykorzystaniem Nixa / Dockera coś takiego powinno dać się klepnąć w godzinę or so).

Jest if na wersję PHP.

Ja chcę napisać test pod to.

Jeśli chcesz mieć 100% pewność, że Twój kod działa na starszym PHPie, to siłą rzeczy musisz uruchomić testy na starszej wersji PHPa - symulacja istnienia/nieistnienia jakiejś globalnej stałej żadnej gwarancji nie daje, bo równie dobrze dokumentacja może być dziurawa, stała mogła być wprowadzona w jakiejś patch-wersji itd.

Noi uruchamiam na każdej z wspieranych, PHP 7.1, 7.2, 7.3, 7.4, 8.0 i 8.1.

O co mi chodzi dokładnie

Widzę że większość ludzi tutaj nadal nie rozumie tego co chcę osiągnąć.

Oświadczam: ja wiem że if na wersję PHP to jest "way to go", taki też kod napisałem.

Ale teraz chcę napisać pod to test, który teraz ma przejść. Jeśli kiedyś zrefaktoruję ten kod z powrotem do checka stałej, to test ma nieprzejść. Chcę napisać test który jest w stanie wykryć czy mój kod nie bierze pod uwagę stałej. I taki test też napisałem, widać go w pierwszym poście. Tylko jego wadą jest to że zostawia stworzoną stałą.

1

Ale teraz chcę napisać pod to test, który teraz ma przejść. Jeśli kiedyś zrefaktoruję ten kod z powrotem do checka stałej, to test ma nieprzejść.

Trochę nie rozumiem sensu takiego testu - jeśli ktoś tworzy stałe kolidujące z PHPowymi, to brzmi na problem tej osoby 😅; czy masz też testy upewniające się, że np. rtkitem nikt przypadkiem nie nadpisał preg_match() na jakąś swoją implementację? Albo że nikt nie nadpisał php_version() aby kłamało?

0
Patryk27 napisał(a):

Ale teraz chcę napisać pod to test, który teraz ma przejść. Jeśli kiedyś zrefaktoruję ten kod z powrotem do checka stałej, to test ma nieprzejść.

Trochę nie rozumiem sensu takiego testu - jeśli ktoś tworzy stałe kolidujące z PHPowymi, to brzmi na problem tej osoby 😅; czy masz też testy upewniające się, że np. rtkitem nikt przypadkiem nie nadpisał preg_match() na jakąś swoją implementację? Albo że nikt nie nadpisał php_version() aby kłamało?

Nie, bo na to nic nie poradzę.

Ale na to że ktoś nadpisał stałą mogę coś poradzić - zamienić check z defined() na check wersji PHP. A jeśli mogę coś poradzić to dodam takiego checka do biblioteki. A skoro dodam go, to muszę go otestować.

0

Działa to inaczej.
Czy testujesz, czy włączona jest biblioteka PCRE dedykowana dla danej wersji? Z tego co pamiętam to można zmienić wersję, tym samym coś w Twojej bibliotece będzie mogło działać inaczej w jakichś nie przewidzianych przypadkach brzegowych.
Czy to że jest dana wersja PHP jest według Ciebie równoznaczne z istnieniem danej stałej?
Jeśli jest równoznaczne to po co testować możliwość jej utworzenia?
Zafiksowaleś się na testowanie tworzenia "tymczasowej" stałej i jakichś sposobów jej usunięcia. Jeśli ciąg przyczynowo skutkowy wskazuje że nie musisz tego robić.
Jeśli faktycznie Twoja funkcja sprawdza wersję i tylko w wypadku danej wersji używa tej stałej to po co testować jej tworzenie.
Jeśli Twoja funkcja nie sprawdza z kolei wersji PHPa to po prostu jest to błąd IMHO.

0
jurek1980 napisał(a):

Działa to inaczej.
Czy testujesz, czy włączona jest biblioteka PCRE dedykowana dla danej wersji? Z tego co pamiętam to można zmienić wersję, tym samym coś w Twojej bibliotece będzie mogło działać inaczej w jakichś nie przewidzianych przypadkach brzegowych.

Tak, ale nie bezpośrednio. Robię to odpalając ten sam zestaw testów na 14 różnych wersjach PHP, dzięki temu wiem że niezależnie od wersji PCRE biblioteka działa tak samo. Samych numerków nie sprawdzam.

Czy to że jest dana wersja PHP jest według Ciebie równoznaczne z istnieniem danej stałej?
Jeśli jest równoznaczne to po co testować możliwość jej utworzenia?

Nie możliwość jej utworzenia, bo to wiem na pewno że się da. Ja testuje to czy jej utworzenie nie wpłynie negatywnie na bibliotekę.

jurek1980 napisał(a):

Jeśli faktycznie Twoja funkcja sprawdza wersję i tylko w wypadku danej wersji używa tej stałej to po co testować jej tworzenie.

Żeby się upewnić że faktycznie sprawdza tylko wersję. Mogłby przecież nadal używać stałej. Po to się pisze testy jednostkowe.

Powiem to prościej

Chcę zabezpieczyć kod przed atakiem "constant override". I zabezpieczyłem. Teraz chcę to przetestować.

Jedynym sposobem przetestowania podatności na jakiś atak, jest spróbować go zrobić w teście i sprawdzić czy się udał.

Dlatego chcę w teście dodać tą stałą, i zobaczyć czy biblioteka rzuca warning. Jeśli nie, to atak się nie udał i test przechodzi.

3

A jeśli mogę coś poradzić to dodam takiego checka do biblioteki. A skoro dodam go, to muszę go otestować.

Poniekąd rozumiem argumentację, lecz IMO jest to gra nie warta świeczki - to trochę jak gdyby robić testy "czy moja funkcja działa po południu" (gdy funkcja w ogóle nie korzysta z czasu), "czy moja funkcja działa gdy zdefiniowany jest BULBUZATOR" (gdy funkcji w ogóle taka stała nie obchodzi) albo "czy moja funkcja działa gdy na serwerze istnieje plik /root/xd" (gdy funkcja w ogóle nie korzysta z systemu plików).

Tak na moje, to testy powinny pokrywać to, na czym funkcja rzeczywiście operuje - a nie to, czego funkcja nie robi / nie potrzebuje; i tak w Twoim przypadku skoro funkcja zależy od wersji PHPa, to testy powinny być oparte właśnie o to - a nie o istnienie/nieistnienie stałej, pliku w systemu plików czy czasu serwera.

0
Patryk27 napisał(a):

A jeśli mogę coś poradzić to dodam takiego checka do biblioteki. A skoro dodam go, to muszę go otestować.

Poniekąd rozumiem argumentację, lecz IMO jest to gra nie warta świeczki - to trochę jak gdyby robić testy "czy moja funkcja działa po południu" (gdy funkcja w ogóle nie korzysta z czasu), "czy moja funkcja działa gdy zdefiniowany jest BULBUZATOR" (gdy funkcji w ogóle taka stała nie obchodzi) albo "czy moja funkcja działa gdy na serwerze istnieje plik /root/xd" (gdy funkcja w ogóle nie korzysta z systemu plików).

Faktycznie, ale nie do końca. Faktycznie nie miałoby sensu sprawdzać randomowych stałych jak np PREG_SPLIT albo PREG_ALLOW_EMPTY.

Ale w moim kodzie używam PREG_UNMATCHED_AS_NULL, i w PHP istnieje konwencja sprawdzania czy funkcjonalność istnieje takim własnie checkiem defined('PREG_UNMATCHED_AS_NULL'). Dlatego moim zdaniem warto to przetestować.

Potencjalny kontrybutor przyjdzie za rok do projektu i się zdziwi "oo, sprawdzanie wersji PHP, ciekawe po co to". Będzie szukał, i znajdzie że to po to żeby sprawdzić czy funkcjonalność istnieje, i wpadnie na pomysł że można "przecież" sprawdzić defined('PREG_UNMATCHED_AS_NULL') bo jest taka konwencja (i bo czystsze, mniej zależne od wersji PHP), tym samym otwierając się na "constant override attack" na starszych PHP'ach. A on pewnie będzie miał najnowszą wersję, więc nie wpadnie na to. PS: 99% szans że tym kontrybutorem będę ja.

Więc muszę napisać test który mnie uchroni przed tym.

I zanim powiesz że mogę napisać komentarz, tak wiem że mogę, wpadłem na to. Ale test jest lepszy.

PS: Swoją drogą @Patryk27 daję Ci nagrodę, bo chyba jako pierwszy w tym wątku skumałeś o co chodzi w temacie.

1

Zgadzam się z @Patryk27 tutaj. Moim zdaniem taki test w ogóle nie ma sensu. Raz ze jest ultra whiteboxowy i zamiast testować "zachowanie" testuje "implementacje" co dla mnie już samo w sobie jest sygnałem że coś jest nie tak. Dwa, że idąc tym tokiem rozumowania można napisać INF testów, dla każdej możliwej stałej albo dla każdego innego warunku, który nijak sie ma do samej funkcji.
Co więcej warunek którego chcesz użyć bezpowrotnie zmienia stan środowiska, więc to jest dla mnie trochę jakbyś chciał napisać test "czy moja funkcja zadziała jak poleci OOM" a potem narzekał że po OOM wszystko się później sypie bo środowisko pozostaje w niestabilnym stanie. Albo "czy moja funkcja zadziała jak skończy się miejsce na dysku" a potem narzekał że wszystkie kolejne testy failują bo nie ma miejsca.

Jeśli koniecznie musisz, to moim zdaniem nie obejdzie się bez jakiejś "magii" w stylu jakiegoś extension, albo odpalania tego konkretnego testu zupełnie "osobno" (co pewnie jest dużo sensowniejszym rozwiązaniem)

0
Shalom napisał(a):

Zgadzam się z @Patryk27 tutaj. Moim zdaniem taki test w ogóle nie ma sensu. Raz ze jest ultra whiteboxowy i zamiast testować "zachowanie" testuje "implementacje" co dla mnie już samo w sobie jest sygnałem że coś jest nie tak. Dwa, że idąc tym tokiem rozumowania można napisać INF testów, dla każdej możliwej stałej albo dla każdego innego warunku, który nijak sie ma do samej funkcji.

Mógłbym, ale to nie ma sensu. Używam jednej stałej. Napiszę więc test, pod to czy ta jedna stała której używam nie ma checka. Nieskończoności innych stałych nie używam, więc nie napiszę testów pod nie :>

Shalom napisał(a):

Co więcej warunek którego chcesz użyć bezpowrotnie zmienia stan środowiska, więc to jest dla mnie trochę jakbyś chciał napisać test "czy moja funkcja zadziała jak poleci OOM" a potem narzekał że po OOM wszystko się później sypie bo środowisko pozostaje w niestabilnym stanie. Albo "czy moja funkcja zadziała jak skończy się miejsce na dysku" a potem narzekał że wszystkie kolejne testy failują bo nie ma miejsca.

Gdybym go napisał "tak o", to tak. Faktycznie zmieniłbym bezpowrotnie stan środowiska.

Hmmm.... Kurcze, gdybym tylko wpadł na to że to jest zły pomysł i ZADAŁ PYTANIE NA FORUM JAK TO PRZETESTOWAĆ NIE PSUJĄĆ BEZPOWROTNIE ŚRODOWISKA. Może wtedy udałoby się coś wymyślić.

Shalom napisał(a):

Jeśli koniecznie musisz, to moim zdaniem nie obejdzie się bez jakiejś "magii" w stylu jakiegoś extension, albo odpalania tego konkretnego testu zupełnie "osobno" (co pewnie jest dużo sensowniejszym rozwiązaniem)

Tak, to były też moje pomysły z postu pierwszego:

TomRiddle napisał(a):

Pomysły jakie mam.

  1. Zhackować PhpUnit żeby odpalał ten konkretny test jako ostatni. Zadziała tak długo jak będzie jeden, bo jak będę chciał więcej to klops.
  2. Odpalać dwa testy w osobnych procesach, tak żeby nie było zdefiniowanej tej stałej, ale to spowolni testy znacznie z 1ms do jakichś 2-3 sekund.
  3. Użyć extensiona "runkit" który może usunąć stałą, ale to ma szereg wad: po pierwsze nie da się łatwo zainstalować runkit na Github Actions, ale to bym ogarnął; ciężko jest zainstalować runkit zarówno na windowsie jak i na linuxie bo to nie jest standardowy extension, i trzeba go specjalnie budować ze źródeł. Dużo zachodu. Jak kontrybutor ściągnie to prędzej się pochlasta niż sobie zbuduje ten extension (bo on np potrzebuje Visual studio redistributables co ma 6gb i nie instaluje się go wcale tak prosto).

[..]

Najbardziej się skłaniam ku opcji 2, ale to jest wolne strasznie.

Aczkolwiek każdy z nich ma jakieś wady, więc zapytałem na forum czy ktoś ma inny pomysł.

1

Btw, zdaje się, że wątki mają unikalne konteksty (w tym stałe), więc może w ten sposób się uda.

0
Patryk27 napisał(a):

Btw, zdaje się, że wątki mają unikalne konteksty (w tym stałe), więc może w ten sposób się uda.

:O O kurde.

Dzieki!

@Patryk27: Ty to jesteś.

0

Aczkolwiek każdy z nich ma jakieś wady, więc zapytałem na forum czy ktoś ma inny pomysł.

Ok to inny pomysł:

  1. Robisz osobną klasę, jakiś PhpRegexSupportHelper która ma w sobie funkcje isFastCośtamSupported
  2. Implementujesz sobie tą funkcje
  3. Piszesz komentarz/doca do tej klasy wyjaśniając czemu sprawdzanie odbywa się w taki a nie inny sposób
  4. Profit!

Pomysł z wątkami wygląda fajnie, dopóki się coś nagle nie zmieni w implementacji ;)

0
Shalom napisał(a):

Ok to inny pomysł:

  1. Robisz osobną klasę, jakiś PhpRegexSupportHelper która ma w sobie funkcje isFastCośtamSupported
  2. Implementujesz sobie tą funkcje
  3. Piszesz komentarz/doca do tej klasy wyjaśniając czemu sprawdzanie odbywa się w taki a nie inny sposób
  4. Profit!

Nic mi to nie da. To nadal tylko komentarz bez testu. Już bym wolał zostawić komentarz bez wydzielania.

Shalom napisał(a):

Pomysł z wątkami wygląda fajnie, dopóki się coś nagle nie zmieni w implementacji ;)

True. Aczkolwiek wolę test z wątkiem, niż brak testu.

0
Patryk27 napisał(a):

Btw, zdaje się, że wątki mają unikalne konteksty (w tym stałe), więc może w ten sposób się uda.

W dupę jeża, wątki są w extensionie pthreads :/

Czyli każdy musiałby wtedy ściągać ten extension specjalnie zeby odpalić testy.

@Patryk27: dupa chyba będzie z tymi wątkami :/

Noi działałoby tylko na instancjach TS z PHP :/ :/ A już się ucieszyłem.

0

Czyli każdy musiałby wtedy ściągać ten extension specjalnie zeby odpalić testy.

Przy tej okazji trudno mi nie pochwalić tutaj Nixa, gdzie zrobiłbyś sobie shell.nix pobierający PHP+pthreads i każdy mógłby odpalić nix-shell, aby trafić do shella z wszystkimi zależnościami dostępnymi na wyciągnięcie ręki :-P

Chcąc mieć taki test, poszedłbym mimo wszystko w stronę tych wątków i wrzucił do README informację oraz dodał jakiegoś Dockerfile'a pozwalającego wszystko przetestować jednym poleceniem; mamy XXI wiek, w końcu! :-)

0

Napiszę po teście asercje sprawdzającą czy stałej nie ma

@TomRiddle: ja bym ten test w takim razie napisał z jakimś ładnym DSLem imitującym closure, w Javie wyglądałoby to jakoś tak:

withXYZConstantDefined(()->
{
   tutaj kod testu
})

A sama funkcja to byłoby coś w stylu

void withXYZConstantDefined(Runnable r){
    // define constant
    r.run();
    // undefine constant, czy coś podobnego
}

I wtedy wiadomo dokładnie jaka była intencja.

Robie tak często w testach w stylu "co jak baza danych przestała działa", coś jak tutaj: https://github.com/Pharisaeus/almost-s3/blob/master/test/src/test/java/net/forprogrammers/almosts3/test/DownloadTest.java#L177

1
Shalom napisał(a):

Napiszę po teście asercje sprawdzającą czy stałej nie ma

@TomRiddle: ja bym ten test w takim razie napisał z jakimś ładnym DSLem imitującym closure, w Javie wyglądałoby to jakoś tak:

[...]
A sama funkcja to byłoby coś w stylu
[...]
I wtedy wiadomo dokładnie jaka była intencja.

No da.

Wiadomo że tak bym zrobił.

1
Patryk27 napisał(a):

Czyli każdy musiałby wtedy ściągać ten extension specjalnie zeby odpalić testy.

Przy tej okazji trudno mi nie pochwalić tutaj Nixa, gdzie zrobiłbyś sobie shell.nix pobierający PHP+pthreads i każdy mógłby odpalić nix-shell, aby trafić do shella z wszystkimi zależnościami dostępnymi na wyciągnięcie ręki :-P

Chcąc mieć taki test, poszedłbym mimo wszystko w stronę tych wątków i wrzucił do README informację oraz dodał jakiegoś Dockerfile'a pozwalającego wszystko przetestować jednym poleceniem; mamy XXI wiek, w końcu! :-)

Nie dobry pomysł imo. Wtedy ktoś musiałby mieć dockera żeby postawić projekt. Poza tym, lokalny PHP daje dużo więcej możliwości i elastyczności niż taki dockerowy. Poza tym, teraz ten projekt jest "vanilla". Klonujesz repo, composer install, composer test i możesz jechać. Nie chcę dodawać konieczności czytania readme, posiadania dockera, spowalniania developmentu kontenerem z php'em, zabierania elastyczności lokalnego sdk, etc.

Dużo zachodu dla pozornie małej wartości. To już lepiej odpalić ten test w osobnym procesie, niż wątku.

Ale @Patryk27 dzięki za super pomysł z wątkiem. Dokładnie z myślą o czymś takim przyszedłem na to forum. Zaakcetowałem Twoją odpowiedź.

Będę szukał dalej jakiegoś innego sposobu jak odpalić z tą stałą tylko ten jeden test.

1 użytkowników online, w tym zalogowanych: 0, gości: 1