niedziela, 28 czerwca 2009

NSIS - Ekslibrisowa ekwilibrystyka czyli piszemy własną bibliotekę

Sława!

Dziś chciałbym jakże szanownym Czytelniczkom i Czytelnikom zaprezentować parę wskazówek jak pisać własne biblioteki/pliki nagłówkowe dla NSIS. Po cóż, a po coż to się męczyć z czymś takim? - zapyta ktoś bardziej marudny. Ha! Otóż marudo moja kochana, po piersze primo zawsze warto wydzielać sobie wieloużywalne kawałki własnego kodu, żeby potem nie wymyślać koła po raz kolejny. A po drugie primo, w wypadku NSISa główny plik projektu przy większej komplikacji ma tendencje do bardzo szybkiego rozrostu, toteż pogubienie się w kodzie jest tylko kwestią czasu. I właśnie aby czas ten wydłużyć warto jednak powydzielać sobie funkcję do bibliotek.

Od czego zacząć? Wydaje mi się, że od wydzielenia, przynajmniej koncepcyjnie, tego co chcemy sobie zamknąć w funkcję w ramach biblioteki (a może bibliotek), oraz wymyśleniu sobie nazw zarówno dla funkcji, jak i dla biblioteki. Należy pamiętać, że nie powinny być zbyt lakoniczne, ani też za długie, bo na dzień dzisiejszy narzędzia wspierające pisanie w NSIS nie podpowiadają składni zawartej w bibliotekach. Jeżeli już wymyśliliśmy sobie to i owo, możemy przystąpić do dzieła (a może nawet Dzieła).

Załóżmy, że nową bibliotekę nazwaliśmy sobie PNG (od Przebłysk Naszego Geniuszu) i zawierać będzię ona jedną funkcję o nazwie, jakżeby inaczej, Funkcja. Jedną bo ma posłużyć za przykład, kolejne sobie możemy dorobić już siłą rozpędu. Dodajmy także, że będziemy chcieli jej używać zarówno w instalatorach jak i deinstalatorach.

Zaczynamy więc od następujacego szkieletu:

!ifndef PNG
    !define PNG

    !verbose push
    !verbose 3

    !ifndef PNG_DELIM
        !define PNG_DELIM '.'
    !endif
    
    !ifndef PNG_UN
        !define PNG_UN
    !endif

 #Includes
 
    !ifndef NFUPREFIX
        !include nfUtils.nsh
    !endif

#Installer-----------------------------------------------------------------------------------------

#Callers-----------------------------------------

#Bodies------------------------------------------

#Unistaller----------------------------------------------------------------------------------------

#Callers-----------------------------------------

#Bodies------------------------------------------

    !verbose pop
!endif

Gdzie, PNG_DELIM określa delimiter między nazwą biblioteki, a nazwą funckji - przyjąłem założenie, że używać będziemy ich w sposób podobny do nsArray, czyli poprzez zapis ${PNG.Funkcja}. Definicja PNG_UN jest podstawą pewnej sztuczki przy wspieraniu deinstalatorów, o czym później może parę słów (Ale niczego nie obiecuję). W sekcji #Includes dodajemy niezbędne polecenia include dołączające zewnętrzne biblioteki (np. LogicLib). Warto dołączać je tutaj, żeby być pewnym, że żadnej nie zabraknie. Włączona tu została biblioteka nfUtils, wspomagająca tworzenie własnych bibliotek. Wreszcie sekcje #Callers zawierać będą "wołacze" do funkcji, czyli to co umożliwi użycie wspomnianego zapisu ${PNG.Funkcja}, a #Bodies już właściwe ciała funkcji.

Uzupełnijmy więc te sekcje dla cześci "instalacyjnej":

#Callers-----------------------------------------

;Funkcja
;
;parametr
;   parametr
;
;zwraca
; wynik
!macro PNG${PNG_DELIM}Funkcja_Call _parametr _wynik
    Push ${_parametr}
    Call PNG${PNG_DELIM}Funkcja
    Pop ${_wynik}
!macroend

#Bodies------------------------------------------
!macro PNG${PNG_DELIM}Funkcja
    !ifndef ${PNG_UN}PNG${PNG_DELIM}Funkcja
        !define ${PNG_UN}PNG${PNG_DELIM}Funkcja '!insertmacro ${PNG_UN}PNG${PNG_DELIM}Funkcja_Call'

        ;$1 - wynik
        ;$0 - parametr
        ${nfu.Function} ${PNG_UN}PNG${PNG_DELIM}Funkcja in $0 out $1 '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ''
         # Ciało funkcji
   
        ${nfu.FunctionEnd}
    !endif
!macroend

W ciale funkcji w $0 znajdzie się przekazywany parametr, natomiast zawartość $1 przy wyjściu z funkcji będzie umieszczona na stosie. Po zakończeniu funkcji wartości $0 i $1 będą takie same jak przed jej wykonaniem. Jeżeli chcemy używać w ciele innych zmiennych $0-9 należy samemu obsłużyć podobne zachowanie za pomocą operacji na stosie.

Zostało więc uzupełnienie sekcji dla deinstalatora, tu już chyba nie ma większej magii:

#Callers-----------------------------------------
!macro un.PNG${PNG_DELIM}Funkcja_Call _parametr _wynik
    Push ${_parametr}
    Call un.PNG${PNG_DELIM}Funkcja
    Pop ${_wynik}
!macroend

#Bodies------------------------------------------
!macro un.PNG${PNG_DELIM}Funkcja
    !undef ${PNG_UN}
    !define ${PNG_UN} 'un.'
    !insertmacro PNG${PNG_DELIM}Funkcja
    !undef ${PNG_UN}
    !define ${PNG_UN}
!macroend

I to było by na tyle. Wnikliwsza analiza, wykaże, że cała zabawa polega na sprytnym wykorzystaniu poleceń preprocesora NSIS do wpływania na proces generowania "wynikowego" źródła do kompilacji, podkładając mu odpowiedni kod w miejsce używanych definicji. Dlatego też, możemy używać naszej biblioteki podobnie jak LogicLib czy nsArray poprzez ${PNG.Funkcja} 'parametr' $Wynik

sobota, 27 czerwca 2009

Parę słów o kontekstem gnanym testowaniu

Sława!

Najnowszy wpis na blogu Jamesa Bacha przypomniał mi, że już dawno miałem wspomnieć parę słów o, nazwijmy to szumnie metodologią, testowaniu gnanym kontekstem (Context-driven-testing). Idea stojąca za tym podejściem jest chyba najbliższa moim intuicyjnym wyczuciom na czym testowanie powinno polegać, i staram się jakoś lepiej lub gorzej wprowadzać sobie w życie, i może coś z tego wyjdzie. Całość opiera się na siedmiu podstawach, które pozwolę sobie przetłumaczyć (pewnie nie najlepiej, ale co mi tam, najwyżej ktoś mądrzejszy mnie poprawi i szlag trafi cały zachwyt ;)):

  1. Wartość każdego postępowania (podejścia, praktyki) zależy od jego kontekstu.
  2. W danym kontekście są dobre praktyki, ale nie ma tej najlepszej.
  3. Ludzie pracujący razem, są najważniejszą częścią w kontekście każdego projektu.
  4. Rozwój projektów, w miarę czasu, często nie podąża przewidywalnymi ścieżkami.
  5. Produkt jest Rozwiązaniem. Jeśli zadany problem nie jest rozwiązany, produkt nie działa.
  6. Dobre testowanie oprogramowania jest wymagającym (i stawiającym wyzwania) procesem intelektualnym.
  7. Tylko rozsądkiem i wprawą, zaprzątniętymi razem podczas całego projektu, jesteśmy zdolni do wykonania właściwych czynności we właściwym czasie, aby skutecznie testować nasze produkty.

Tylko tyle, i aż tyle, bo jak pokazuje nie raz bliższa lub dalsza praktyka, nie wszystkim poszczególne punkty wydają się prawdziwe, a szkoda.

Po więcej informacji (w języku angielskim) zapraszam na stronę http://www.context-driven-testing.com/ , gdyż na prawdę warto się zapoznać. Nawet jeżeli uznacie, że sobie można o kant czterech rozbić.