( rednio) zaawansowane programowanie w C++ (ZPR) Wykªad 13 - tworzenie oprogramowania, testowanie, powtórzenie Robert Nowak 2015L ( rednio) zaawansowane programowanie w C++ (ZPR) 1/45
Metodologie lekkie (agile) ( rednio) zaawansowane programowanie w C++ (ZPR) 2/45
Pewne fakty zwi zane z tworzeniem oprogramowania Najcz stsze przyczyny niepowodzenia projektu (przedstawione na wykªadzie 1): niestabilne wymagania optymistyczna estymacja kosztów (gªównie czasu) realizacji projektów R. Glass, Frequently Forgotten Facts about Software Engineering, 2001 Sterowanie projektem: koszt czas jako± zakres ( rednio) zaawansowane programowanie w C++ (ZPR) 3/45
Model wodospadowy Wodospadowy (tradycyjny) model tworzenia oprogramowania: 1. analiza 2. projektowanie Koszt 3. implementacja 4. testowanie 5. wdro»enie 6. piel gnacja Wady: du»e ryzyko niepowodzenia projektu dªugi czas sprz»enia zwrotnego Przyczyny: Czas wykªadniczy (wzgl dem czasu) wzrost kosztów zmian w projekcie. ( rednio) zaawansowane programowanie w C++ (ZPR) 4/45
Metodologie lekkie (extreme programming, SCRUM) Mo»liwo±ci uzyskania innej krzywej kosztu zmian w czasie: prostota projektu automatyczne testy nastawienie na ci gªe zmiany w projekcie polepszenie komunikacji Wªa±ciwo±ci: krótki czas sprz»enia zwrotnego Koszt mniejsze ryzyko niepowodzenia projektu Czas ( rednio) zaawansowane programowanie w C++ (ZPR) 5/45
Cykl tworzenia oprogramowania - SCRUM ( rednio) zaawansowane programowanie w C++ (ZPR) 6/45
Zmniejszenie kosztów zmian - implementacja Utrzymywanie dziaªaj cej wersji przez caªy czas: automatyczne testowanie: moduªów funkcjonalne ci gªa integracja Utrzymywanie kodów ¹ródªowych dobrej jako±ci zmiana struktury programu bez zmiany funkcjonalno±ci (refactoring) wzorce projektowe komentarze (komentarz mówi DLACZEGO, kod mówi JAK) ( rednio) zaawansowane programowanie w C++ (ZPR) 7/45
Zmniejszanie kosztów zmian (2) - zarz dzanie denicja tego co to znaczy,»e zadanie zostaªo wykonane (projekt, implementacja, testy jednostkowe, testy funkcjonalne, wdro»enie) jawne sterowanie projektem jawna lista zada«kolektywne prawo do zmian kodu (narz dzia zarz dzaj ce wersjami) kolektywne szacowanie zªo»ono±ci zada«nastawienie na zmiany planowanie czasu pracy dyskusja z u»ytkownikiem lub wªa±cicielem biznesowym podsumowania, wyci ganie wniosków ( rednio) zaawansowane programowanie w C++ (ZPR) 8/45
PRINCE2 - projekty w sterowalnym ±rodowisku zarz dzanie strategiczne: odpowiedzialne za sukces projektu, m.in. ci gªa zasadno± biznesowa zarz dzanie operacyjne: delegowanie zada«, monitorowanie, alokacja zasobów ( rednio) zaawansowane programowanie w C++ (ZPR) 9/45
( rednio) zaawansowane programowanie w C++ (ZPR) 10/45
Testowanie moduªów: zagadnienia ogólne Rola Testowania: klient upewnia si,»e projekt dziaªa zgodnie z zaªo»eniami programista upewnia si,»e kod dziaªa tak jak on zakªada implementacja testów: jedna z pocz tkowych faz implementacji nowej klasy czy biblioteki pisanie testów powinno by proste wiele maªych testów (test-case), które mo»na grupowa (test-suits) mo»liwo± wyboru ilo±ci wy±wietlanej informacji w razie niepowodzenia testu mo»liwo± ±ledzenia post pu przy du»ych i zªo»onych testach biblioteki: boost::test, cppunit, inne ( rednio) zaawansowane programowanie w C++ (ZPR) 11/45
Tworzenie testów - poziomy boost/test/test_tools.hpp - dostarcza zestawu makrodenicji poziom informacja licznik bª dów wykonywanie testu WARN ostrze»enie bez zmian kontynuowane CHECK bª d zwi kszany kontynuowane REQUIRE bª d krytyczny zwi kszany przerywane zazwyczaj nale»y u»ywa CHECK (tak jak asercji) WARN gdy mniej istotne ni» poprawno± (np. wydajno± ) REQUIRE gdy kontynuacja testu nie ma sensu int* ptr = 0L; BOOST_WARN( sizeof(short) == sizeof(int) ); //ostrze»enie BOOST_CHECK( silnia(1) == 1 ); BOOST_REQUIRE( ptr!= 0L ); BOOST_CHECK( *ptr == 2 ); //Ta linia nigdy si nie wykona ( rednio) zaawansowane programowanie w C++ (ZPR) 12/45
Tworzenie testów - warunki liczbowe BITWISE_EQUAL(a,b) zgodno± poszczególnych bitów EQUAL(a,b) równo± (woªany operator==) SMALL(val,ɛ) dla zmiennopozycyjnych, warto± bezwzgl dna mniejsza ni» ɛ CLOSE(a,b,ɛ) dla zmiennopozycyjnych, warto±ci bezwzgl dna ró»nicy nie wi ksza ni» ɛ% int a = 1, b = 2; //przykªady testów double x = 1.0, y = 1.001; BOOST_CHECK_BITWISE_EQUAL(0x27, 0x17); //ró»nica na 4 oraz 5 bicie BOOST_CHECK_EQUAL(a,b); BOOST_CHECK_SMALL( x - y, 0.001); // x - y <= 0.001 BOOST_WARN_CLOSE(x, y, 0.1); // (x-y)/x <= 0.001 oraz (x - y)/y <= 0.001 ( rednio) zaawansowane programowanie w C++ (ZPR) 13/45
Tworzenie testów - wyj tki NO_THROW( stat ) THROW(stat, except) EXCEPTION(stat, except, p) nie powinno generowa wyj tków rzuca wyj tkiem danego typu spodziewane gen. wyj tku, który speªnia predykat struct MyException : public std::exception {//przykªadowy wyj tek int i_; MyException(int i) : i_(i) {} }; void f() { throw MyException(1); } //testowany kod //przykªadowy warunek dla wyj tku bool iscritical(const MyException& ex) { return ex.i_ == 2; } BOOST_CHECK_NO_THROW( f() ); //nie powinno rzuca wyj tków BOOST_REQUIRE_THROW( f(), std::exception );//spodziewany wyj tek //spodziewany wyj tek odp. typu oraz speªniaj cy warunek BOOST_CHECK_EXCEPTION( f(), MyException, iscritical ); ( rednio) zaawansowane programowanie w C++ (ZPR) 14/45
Generowane wiadomo±ci warning in <nazwa>: condition <opis> is not satised error in <nazwa>: test <opis> failed fatal error in <nazwa>: critical test <opis> failed MESSAGE( p, msg ) je»eli warunek faªszywy to generuje wiadomo± 'msg' int i = 1; BOOST_CHECK_MESSAGE(i == 2,"mialo byc i == 2, a jest i == " << i); //BOOST_ERROR( msg ) = BOOST_CHECK_MESSAGE( false, msg ) BOOST_ERROR("nie powinien sie tutaj znalezc"); //BOOST_FAIL( msg ) = BOOST_REQUIRE_MESSAGE( false, msg ) BOOST_FAIL("dalsze wykonywanie testu nie ma sensu"); ( rednio) zaawansowane programowanie w C++ (ZPR) 15/45
Test - obiekt klasy test_case #define BOOST_TEST_CASE( fun ) \ make_test_case(callback0<>(fun),boost_test_stringize(fun)) void test_1 () { //tutaj co± testuje } BOOST_TEST_CASE( &test_1 );//tworzy obiekt typu test_case BOOST_AUTO_TEST_CASE( nazwa ){ //tworzy obiekt typu test_case //tutaj co± testuje } ( rednio) zaawansowane programowanie w C++ (ZPR) 16/45
Organizacja testów - zbiór testów (test_suite) //Reprezentuje zbiór testów (wzorzec kompozytu) class test_suite : public test_case { public: void add( test_case* tc, //Metoda dodaje nowy test do zbioru unit_test_counter expected_failures = 0, int timeout = 0 ); //np. do wykrywania p tli niesk. /*... */ }; //Makrodefinicja tworz ca test_suite BOOST_TEST_SUITE( nazwa ); //Przykªad u»ycia test_suite* init_unit_test_suite( int, char* [] ) { //Zamiast main test_suite* test= BOOST_TEST_SUITE("nazwa"); test->add(boost_test_case(&test1)); test->add(boost_test_case(&test2)); return test; } ( rednio) zaawansowane programowanie w C++ (ZPR) 17/45
Zarz dzanie testami - Unit Test Framework linia komend zmienne ±rodowiskowe (np. BOOST_TEST_LOG_LEVEL ) kod testów (klasa unit_test_log, singleton) nazwa opcje opis build_info yes no platforma, itp. log_level all... warning error... ilo± wy±w. komunikatów nothing report_level no conrm short detailed wielko± komunikatu output_format HRF XML HRF: dla czªowieka show_progress yes no pokazuje post p random 0 1 2... testy losowo mytest --build_info=yes --log_level=all --report_level=detailed mytest --show_progres=yes --log_level=nothing ( rednio) zaawansowane programowanie w C++ (ZPR) 18/45
boost/test/minimal.hpp nie potrzebuje biblioteki binarnej boost_unit_test_framework dostarcza BOOST_CHECK(p) BOOST_REQUIRE(p) BOOST_ERROR(msg) BOOST_FAIL(msg) //Przykªad u»ycia #include <boost/test/minimal.hpp> int test_main(int, char* [] ) { //tak musi si nazywa gªówna funkcja BOOST_CHECK(1 == 2 ); BOOST_ERROR("blad"); BOOST_FAIL("blad krytyczny"); return 0; } ( rednio) zaawansowane programowanie w C++ (ZPR) 19/45
Opis narz dzia SCons www.scons.org opis projektu to skrypt Pythona bardzo ªatwo rozszerzalny automatyczna analiza zale»no±ci wsparcie dla: C++, Java, Fortran, Yacc, Lex, Qt, LaTeX, Subversion, msvc (zast puje.vcproj,.sln) i in. Linux, AIX, Windows NT, Mac OS X, Solaris i in. wsparcie dla kompilacji równolegªej detekcja zmiany zawarto±ci plików przy u»yciu sygnatury MD5 (lub tradycyjnie timestamps) ( rednio) zaawansowane programowanie w C++ (ZPR) 20/45
SCons - tworzenie aplikacji # plik SConstruct Program(source=['calc.cpp', 'main.cpp'], target = prog) Windows (Visual Studio Command Prompt): cl /Fo calc.obj /c calc.cpp /nologo cl /Fo main.obj /c main.cpp /nologo link /nologo /OUT:prog.exe calc.obj main.obj Linux (gnu g++): g++ -o calc.o -c calc.cpp g++ -o main.o -c main.cpp g++ -o prog calc.o main.o ( rednio) zaawansowane programowanie w C++ (ZPR) 21/45
SCons - tworzenie biblioteki # plik SConstruct SharedLibrary(source=['x.cpp', y.cpp'], target = bib) Windows (Visual Studio Command Prompt): cl /Fo x.obj /c x.cpp /nologo cl /Fo y.obj /c y.cpp /nologo link /nologo /dll /out:bib.dll /implib:bib.lib x.obj y.obj Linux (gnu g++): g++ -o x.os -c -fpic x.cpp g++ -o y.os -c -fpic y.cpp g++ -o libbib.so -shared x.os y.os ( rednio) zaawansowane programowanie w C++ (ZPR) 22/45
System zarz dzania wersjami, repozytorium serwer plików, przechowuje kolejne wersje plików i katalogów, umo»liwia wycofywanie zmian i prac grupow ( rednio) zaawansowane programowanie w C++ (ZPR) 23/45
Repozytorium - blokowanie ( rednio) zaawansowane programowanie w C++ (ZPR) 24/45
Repozytorium - rozstrzyganie koniktów ( rednio) zaawansowane programowanie w C++ (ZPR) 25/45
Repozytorium - serwer lokalny i zdalny (publikowanie) local push server update plik pull plik commit A plik ( rednio) zaawansowane programowanie w C++ (ZPR) 26/45
Debugger program sªu» cy do uruchamiania innych programów zatrzymywania dziaªania badanego programu w okre±lonym miejscu wykonywanie pojedynczych instrukcji wy±wietlanie zawarto±ci zmiennych Przykªady: GNU debugger (gdb), Microsoft Visual Studio, inne ( rednio) zaawansowane programowanie w C++ (ZPR) 27/45
Narz dzie do wysyªania komunikatów (Boost.Log) badaniu stanu aplikacji cz sto jedyny dost pny mechanizm do ±ledzenia bª dów (gdy nie ma mo»liwo±ci u»ycia debuggera) #include <boost/log/trivial.hpp> int main(int, char*[]) { BOOST_LOG_TRIVIAL(debug) << "A debug severity message"; BOOST_LOG_TRIVIAL(info) << "An informational severity message"; BOOST_LOG_TRIVIAL(warning) << "A warning severity message"; BOOST_LOG_TRIVIAL(error) << "An error severity message"; return 0; } obserwatorzy: konsola, plik, NT Event Loggers, UNIX Syslog alternatywne rozwi zania: pahteios, glog, log4cxx, log4cpp ( rednio) zaawansowane programowanie w C++ (ZPR) 28/45
powtórzenie przykªadowe zadania Powtórzenie ( rednio) zaawansowane programowanie w C++ (ZPR) 29/45
powtórzenie przykªadowe zadania Powtórzenie w tki, boost::thread obiekty wspóªdzielone wy±cigi synchronizacja (mutex), zakleszczenia (deadlock) pasywny obiekt (monitor) aktywny obiekt szablony, programowanie generyczne szablony jako mechanizm czasu kompilacji specjalizacja szablonu TMP (template metaprogramming) informacja o typie w szablonach (trejty) sposoby na zmniejszanie nadmiernego rozrastania si kodu sposoby na nieczytelne komunikaty o bª dach ( rednio) zaawansowane programowanie w C++ (ZPR) 30/45
powtórzenie przykªadowe zadania Powtórzenie (2) biblioteka standardowa i biblioteki boost kontenery kontenery sekwencyjne, asocjacyjne i haszuj ce (bazuj ce na tablicach, bazuj ce na w zªach) std::pair, std::tuple std::function boost::variant, boost::optional, boost::any boost::multi_array boost graph library algorytmy modykuj ce i niemodykuj ce: wyszukiwanie, sortowanie, usuwanie ograniczenie potrzeby pisania funktorów (std::bind) funkcje anonimowe (funkcje lambda) warstwa trwaªo±ci wbudowany SQL serializacja (boost::serialization) ( rednio) zaawansowane programowanie w C++ (ZPR) 31/45
powtórzenie przykªadowe zadania szablony, rozrost kodu //dwuwymiarowa tablica o ustalonych rozmiarach template<typename T, unsigned int m1, unsigned int m2> class Array2D { public: Array2D() {} Array2D operator+=(const Array2D&); //wiele innych metod private: T tab_[m1*m2]; }; W aplikacji u»ywa si powy»szego szablonu dla wielu ró»nych rozmiarów. Zmie«struktur wzorca, aby zmniejszy ilo± generowanego kodu. ( rednio) zaawansowane programowanie w C++ (ZPR) 32/45
powtórzenie przykªadowe zadania rozrost kodu - rozwi zanie //Usuni cie parametrów szablonu, które nie s typami template<typename T> struct Array2DImpl { Array2DImpl(unsigned int m1, unsigned int m2, T* tab) : m1_(m1), m2_(m2), tab_(tab) {} unsigned int m1_; unsigned int m2_; T* tab_; }; template<typename T, unsigned int m1, unsigned int m2> class Array2D : private Array2DImpl<T> { public: Array2D() : Array2DImpl<T>(m1,m2,tab_) {} Array2D operator+=(const Array2D&); private: T tab_[m1*m2]; }; ( rednio) zaawansowane programowanie w C++ (ZPR) 33/45
powtórzenie przykªadowe zadania wy±cig struct Thread { Thread(MTCounter counter) : c_(counter) {} void operator()() { for(int i=0;i<1000000;++i) c_.inc(); } MTCounter c_; }; int main() { MTCounter counter; Thread t1(counter), t2(counter); boost::thread thrd1( ref(t1) ); boost::thread thrd2( ref(t2) ); thrd1.join(); thrd2.join(); return 0; } ( rednio) zaawansowane programowanie w C++ (ZPR) 34/45
powtórzenie przykªadowe zadania struct Counter { Counter() : i_(0) {} int get() { return i_; } void set(int i) { i_ = i; } int i_; }; class MTCounter { public: MTCounter() : counter_(new Counter) {} MTCounter(const MTCounter& c) : counter_(c.counter_) {} void inc() { mutex::scoped_lock scoped(m_); counter_->set( counter_->get() + 1 ); } int get() const { mutex::scoped_lock scoped(m_); return counter_->get(); } private: shared_ptr<counter> counter_; mutable mutex m_; }; ( rednio) zaawansowane programowanie w C++ (ZPR) 35/45
powtórzenie przykªadowe zadania struct CounterSync { CounterSync() : i_(0) {} int get() { return i_; } void set(int i) { i_ = i; } int i_; mutex m_; }; class MTCounterSync { public: MTCounterSync() : counter_(new CounterSync) {} MTCounterSync(const MTCounterSync& c) : counter_(c.counter_) {} void inc() { mutex::scoped_lock scoped(counter->m_); counter_->set( counter_->get() + 1 ); } int get() const { mutex::scoped_lock scoped(counter->m_); return counter_->get(); } private: shared_ptr<countersync> counter_; }; ( rednio) zaawansowane programowanie w C++ (ZPR) 36/45
powtórzenie przykªadowe zadania szablony, trejty CheckInit : nie pozwala zainicjowa warto±ci 0 ( wyj tek ) CheckDeref: gdy dereferencja i pusty wska¹nik to wyj tek CheckAll: oba na raz NoCheck: brak sprawdzania typedef SmartPtr<int,NoCheck> PNoCheck; typedef SmartPtr<int,CheckInit> PInitCheck; typedef SmartPtr<int,CheckDeref> PDerefCheck; typedef SmartPtr<int,CheckAll> PAllCheck; BOOST_CHECK_NO_THROW( PNoCheck(0L) ); BOOST_CHECK_THROW( PInitCheck(0L), NullPointerException ); BOOST_CHECK_NO_THROW( PDerefCheck(0L) ); BOOST_CHECK_THROW( PAllCheck(0L), NullPointerException ); PNoCheck p1(0l); PDerefCheck p2(0l); BOOST_CHECK_NO_THROW( p1.get() ); BOOST_CHECK_THROW( p2.get(), NullPointerException ); ( rednio) zaawansowane programowanie w C++ (ZPR) 37/45
powtórzenie przykªadowe zadania struct NullPointerException : public std::exception {}; template<typename T,typename Checking = NoCheck > class SmartPtr { public: SmartPtr(T* p) : p_(p) { //if( Checking == CheckInit Checking == CheckAll ) //check(); } T& get() { //if( Checking == CheckDeref Checking == CheckAll ) //check(); return *p_; }; private: void check() const { (!p_) throw NullPointerException(); } T* p_; }; ( rednio) zaawansowane programowanie w C++ (ZPR) 38/45
powtórzenie przykªadowe zadania struct NoCheck { }; //trejt struct CheckInit : public NoCheck {}; //trejt struct CheckDeref : public NoCheck { };//trejt struct CheckAll : public CheckInit, public CheckDeref { };//trejt template<typename T,typename Checking = NoCheck > class SmartPtr { public: SmartPtr(T* p) : p_(p) { init(checking()); } T& get() { deref(checking()); return *p_; } private: void init(checkinit){ check();} void init(nocheck){ } void deref(checkderef){ check();} void deref(nocheck){ } void check() const { if(!p_) throw NullPointerException(); } T* p_; }; ( rednio) zaawansowane programowanie w C++ (ZPR) 39/45
powtórzenie przykªadowe zadania znajomo± kodu kontenery: std::array, std::vector, std::list, std::set, std::map, boost::any, boost::variant, boost::optional, std::function algorytmy: std::for_each, std::nd, std::nd_if, std::equal_range, std::copy, std::remove, std::transform iteratory: std::insert_iterator (inserter), std::ostream_iterator inne: std::bind, std::ref, std::cref, funkcje anonimowe (lambda) ( rednio) zaawansowane programowanie w C++ (ZPR) 40/45
powtórzenie przykªadowe zadania znajomo± kodu: podaj wydruk //Podaj wydruk typedef pair<int,string> P; const P TAB[] = { P(3,"A"), P(3,"M"), P(2,"L"), P(1,"A") }; const int TAB_SIZE = sizeof(tab)/sizeof(tab[0]); typedef variant<int,string> Var; set<var> svar; transform(tab, TAB + TAB_SIZE, inserter(svar, svar.begin() ), bind(&p::first, _1) ); cout << svar.size() << *svar.begin() << endl; ( rednio) zaawansowane programowanie w C++ (ZPR) 41/45
powtórzenie przykªadowe zadania znajomo± kodu: podaj wydruk struct CountFun { CountFun() : counter_(0) {} template<typename T> void inc(const any& a) { if(a.type() == typeid(t) ) ++counter_; } int counter_; }; vector<any> v; v.push_back(1); v.push_back(2.54); v.push_back(string("ala")); v.push_back(1.01); CountFun counter; for_each(v.begin(), v.end(), bind(&countfun::inc<double>, ref(counter),_1) ); cout << counter.counter_ << endl; ( rednio) zaawansowane programowanie w C++ (ZPR) 42/45
powtórzenie przykªadowe zadania znajomo± kodu: podaj wydruk void fxx(int& count, const string& s, int offset) { count += s.size() + offset; } typedef boost::function<void (int)> PF; int x = 0; vector<pf> vpf; vpf.push_back( bind(fxx, ref(x), string("a"), _1) ); vpf.push_back( bind(fxx, ref(x), string("aa"), _1) ); vpf.push_back( bind(fxx, ref(x), string("aaa"), _1) ); for_each( vpf.begin(), vpf.end(), bind(&pf::operator(),_1,1) ); cout << x << endl; ( rednio) zaawansowane programowanie w C++ (ZPR) 43/45
powtórzenie przykªadowe zadania problemy projektowe Przedstawiony kod jest bª dny. Dla N = 5 (linia 4) dziaªa on poprawnie, natomiast po zmianie na N = 500 wyst puj bª dy w linii 10 lub 11. Co Twoim zdaniem jest ¹ródªem problemu? vector<foo> kolekcja; typedef vector<foo>::const_iterator Id; map<int,id> index; static const int N = 500; for(int i=0; i < N; ++i ) { Id id = kolekcja.insert( kolekcja.end(), Foo(i) ); index.insert( make_pair(i,id) ); } Id id_0 = (*index.find(0)).second; const Foo& f = *id_0; cout << f.i_ << endl; ( rednio) zaawansowane programowanie w C++ (ZPR) 44/45
powtórzenie przykªadowe zadania Dodatkowa lektura (http://staff.elka.pw.edu.pl/~rnowak2/rnbib.html): R. Nowak, Badanie pokrycia kodu c++ testami jednostkowymi, Programista, no. 6, pp. 10 13, 2012. Dzi kuj ( rednio) zaawansowane programowanie w C++ (ZPR) 45/45