Warning: strpos() [function.strpos]: Empty needle in /home/dealer/kasat/pkg/cahir/web/html/lib/plugins/translation2/action.php on line 53

Warning: Cannot modify header information - headers already sent by (output started at /home/dealer/kasat/pkg/cahir/web/html/lib/plugins/translation2/action.php:53) in /home/dealer/kasat/pkg/cahir/web/html/inc/actions.php on line 163
erlang:eunit_edoc [Krystian Bacławski Wiki]
 

Testowanie

Źródła

Podręczniki:

Moduły:

  • eunit - interfejs użytkownika do uruchamiania testów
  • common_test - opis wywołań zwrotnych modułu implementującego testy
  • ct - podstawowy interfejs do programowania testów
  • ct_rpc - testowanie przy pomocy modułu rpc

Narzędzia:

  • run_test - uruchamianie testów z linii poleceń

Typowanie

Typy

Lista typów

Typ Znaczenie
any() dowolne wyrażenie erlangowe
none() pusty typ, “zwracany” przez funkcje, które nigdy nie wychodzą
pid() identyfikator procesu
port() port
ref() referencja
nil() pojedynczy element - pusta lista []
atom() wszystkie atomy erlangowe
binary() wszystkie ciągi binarne postaci «_:_ * 8»
float() liczby zmiennoprzecinkowe
integer() liczby całkowite
fun() wszystkie możliwe funkcje
list(T) lista przechowująca wartości typu T
tuple() wszystkie możliwe krotki
Synonim Typ
term() any()
bool() 'false' | 'true'
byte() 0..255
char() 0..16#10ffff
non_neg_integer() 0..
pos_integer() 1..
neg_integer() ..-1
number() integer() | float()
list() [any()]
string() [char()]
nonempty_string() [char(),…]
module() atom()
mfa() {atom(),atom(),byte()}
node() atom()
timeout() 'infinity' | non_neg_integer()
no_return() none()

Typy złożone

Krotki

Tuple :: {} | {TList}        % pusta lub zawiera listę
TList :: Type | Type, TList  % typ i ogon z typami

Przykład

{}
{ string(), integer(), atom() }

Przedziały

Możemy również wyrażać dowolne przedziały otwarte lub zamknięte:

Zapis Znaczenie
..42 mniejsze równe 42
666.. większe równe 666
-10..10 od -10 do 10

Ciągi binarne

Zapis Znaczenie
«» pusty
«_:pos_integer()» o podanym rozmiarze
«_:_*pos_integer()» o podanym rozmiarze jednostki
«_:pos_integer(), _:_*pos_integer()» złożony

Funkcje

Możemy uściślać postać funkcji – czyli zawężać typy argumentów i wyniku:

Zapis Znaczenie
fun( (…) → Type ) dowolna ilość argumentów, określony typ wyniku
fun( () → Type ) funkcja bezargumentowa, określony typ wyniku
fun( (TList) → Type ) określone typy argumentów jak i wyniku

Dodawanie informacji o typach

Deklarowanie nowych typów

Dokonujemy przy pomocy dyrektywy -type:

Przykład

-type my_type() :: Type.  % ogólna postać
 
-type item(Key, Val) :: [{Key, Val}].  % to samo co list({Key, Val})

Typowanie pól rekordów

Definicję rekordu można rozszerzać o typy pól:

Przykład

-record(rec, {field1 :: Type1,
              field2,            % równoważne field2 :: any()
              field3 :: Type3}).
 
-record(rec, {field1 = [] :: Type1,  % najpierw wartość domyślna, potem typ
              field2,
              field3 = 42 :: Type3}).
 
-record(rec, {f1 = 42 :: integer(),
              f2      :: float(),    % typ każdego pola bez wartości domyślnej jest łączony z atomem 'undefined'
              f3      :: 'a' | 'b'). % równoważne 'undefined' | 'a' | 'b'

Po deklaracji rekordu można go używać jako typu:

#rec{}
#rec{some_field :: Type}  % nadpisuje oryginalny typ (z definicji rekordu) danego pola

Kontrakty / specyfikacje funkcji

Możemy opisać jakiego typu powinny być wartości argumentów funkcji i jej wynik, stosując dyrektywę -spec.

-spec Module:Function(ArgType1, ..., ArgTypeN) -> ReturnType.
-spec Function(ArgType1, ..., ArgTypeN) -> ReturnType.           % specyfikacja i funkcja w tym samym pliku
-spec Function(ArgName1 :: Type1, ..., ArgNameN :: TypeN) -> RT. % argumenty nazwane

A co z wieloma klauzulami funkcyjnymi?

-spec foo(T1, T2) -> T3;  % Uwaga! Para typów (T1, T2) powinna być istotnie różna od (T4, T5)
         (T4, T5) -> T6.

Przykład

-spec update(Key :: term(), Fun :: fun((term()) -> term()), orddict()) -> orddict().
-spec update(Key :: term(), Fun :: fun((term()) -> term()), term(), orddict()) -> orddict().

Typy mogą posiadać zasięg modułu!

Znalezione w dialyzer.erl

-spec plt_info(file:filename()) ->
     {'ok', [{'files', [file:filename()]}]} | {'error', atom()}.

W specyfikacji kontraktów funkcji można używać nowego strażnika is_subtype/2!

-spec id(X) -> X when is_subtype(X, tuple()).
 
-spec foo({X, integer()}) -> X when is_subtype(X, atom());
         ([Y]) -> Y when is_subtype(Y, number()).

Znalezione w lists.erl

-spec keysort(pos_integer(), [T]) -> [T] when is_subtype(T, tuple()).

TypEr

TypEr jest narzędziem do inferencji typów o sprawdzania poprawności anotacji -spec.

TypEr nie wykrywa dokładnych typów, a tylko takie dla których aplikacja funkcji się powiedzie (jej wykonanie już niekoniecznie). Innymi słowy: TypEr znajduje zbiór krotek typów taki, że jego domknięcie nie umożliwia zaaplikowania do funkcji.

Niech $$S, T$$ są znalezionymi typami funkcji $$f(S) → T$$. Zaaplikujmy term $$E$$ do $$f$$, jeśli aplikacja i obliczenie się powiodło to: $$f(E) → V$$, $$E \leq S$$, $$V \leq T$$.

Najnowsza wersja programu TypEr wymaga bazy danych wygenerowanej przez program Dialyzer.

Przed dodaniem typowania i kontraktów

$ typer --show usr.erl usr_db.erl
...
%% File: "usr_db.erl"
%% ------------------
-spec create_tables(_) -> any().
-spec close_tables() -> any().
-spec add_usr(#usr{}) -> 'ok'.
-spec update_usr([tuple()] | tuple()) -> 'ok'.
-spec delete_usr(_) -> 'ok' | {'error','instance'}.
...

Po dodaniu typowania i kontraktów

...
%% File: "usr_db.erl"
%% ------------------
-spec create_tables(string()) -> {'ok',ref()} | {'error',atom()}.
-spec close_tables() -> 'ok' | {'error',atom()}.
-spec add_usr(#usr{}) -> 'ok'.
-spec update_usr(#usr{}) -> 'ok'.
-spec delete_usr(integer()) -> 'ok' | {'error',atom()}.
-spec delete_usr(integer(),integer()) -> 'ok' | {'error',atom()}.
...

Gdybyśmy teraz próbowali wpisać złą specyfikację funkcji add_usr/1:

-spec(add_usr(#usr{}) -> integer()).

To dostaniemy błąd:

typer: Error in contract of function usr_db:add_usr/1
  The contract is: (#usr{}) -> integer()
  but the inferred signature is: (#usr{}) -> 'ok'

Dialyzer

Program Dialyzer służy do statycznej analizy kodu erlangowego – wykrywania niektórych błędów programistycznych:

  • funkcji, które nie powracają,
  • nieużywanych funkcji,
  • konstrukcja niepoprawnych list,
  • aplikacje funkcji, które zawiodą,
  • klauzule dopasowania, które są nieużywane lub na pewno nie dopasują wyrażenia,
  • funkcje, które wychodzą wyłącznie poprzez rzucenie wyjątku,
  • funkcje, których zwracana wartość nie zgadza się z sygnaturą kontraktu,
  • niewystarczająco wyspecyfikowane funkcje (specyfikacja pozwala na więcej niż znalezione automatycznie typowanie),
  • zbyt agresywanie wyspecyfikowane funkcje (specyfikacja pozwala na mniej niż znalezione automatycznie typowanie).
Przebieg Dialyzera
# dialyzer -c usr.erl usr_db.erl
Checking whether the PLT /home/cahir/.dialyzer_plt is up-to-date... yes
Proceeding with analysis...
usr.erl:110: The pattern [] can never match the type {'error','instance'}
done in 0m0.33s
done (warnings were emitted)
110: -spec(set_service(integer(), service(), bool()) -> ok | {error, instTime()}).
111:
112: set_service(CustId, Service, Flag) ->
113:     call({set_service, CustId, Service, Flag}).
-type(instTime() :: instance | timeout).
 
call(Request) ->
    Ref = make_ref(),
    ?MODULE! {request, {self(), Ref}, Request},
    receive
    {reply, Ref, Reply} -> Reply
    after
    ?TIMEOUT -> {error, timeout}
    end.
Baza danych PLT

Program Dialyzer do wygenerowania bazy danych z AST biblioteki wymaga, by wszystkie pliki biblioteki zostały skompilowane z opcją +debug_info.

Bazę danych należy wygenerować poniższym poleceniem:

# dialyzer --build_plt --apps erts kernel stdlib mnesia

W zależności od potrzeb możemy dogenerować informacje o wymaganych bibliotekach np.:

# dialyzer --add_to_plt --apps crypto

Źródła

Podręczniki:

Narzędzia:

  • The Dialyzer - a DIscrepancy AnalYZer for ERlang program
  • TypEr - A Type Annotator of Erlang Code

Dokumentacja

Źródła

Podręczniki:

Moduły:

  • edoc - the Erlang program documentation generator
 
erlang/eunit_edoc.txt · Last modified: 2010/06/15 14:02 by Krystian Bacławski
 
Except where otherwise noted, content on this wiki is licensed under the following license:CC Attribution-Noncommercial-No Derivative Works 3.0 Unported
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki