31 sierpnia 2014

[python] mock_open - testowanie uchwytu do pliku

Coś czego brakowało mi podczas pisania skryptów, czyli metody na zmockowanie bibliotecznej funkcji open (do otwierania plików). Framework do mocków posiada specjalną metodę mock.mock_open(), jednak wszystkie przykłady jakie znalazłem na sieci pokazywały działanie tego mechanizmu w interpreterze, co jakoś nie współgrało z moimi TestSuite-ami. Wydaje mi się, że da się to rozwiązać w bardziej elegancji sposób, ale póki co jest to coś co działa.

Przykład: zapisywanie i odczytywanie z pliku:
#! /usr/bin/env python3
import os

def fileWrite(fileName):
    f = open(fileName, 'w+')
    f.write('text\n')
    f.close()

def fileRead(fileName):
    f = open(fileName, 'r')
    count = 0
    for _ in f:
        count += 1
    f.close()
    return count
Po pierwsze nie udało mi się skorzystać z patch za pomocą dekoratora, a jedynie przez with. Także wywołanie jest troszkę inne, jako drugi parametr, przekazywany jest mock, stworzony przez mock.mock_open() (zamiast domyślnie wygenerowanego z MagicMock). Parametr create odpowiada za wygenerowanie atrybutów, jego brak sprawia, że zgłaszany jest brak atrybutu asdf.open() przez test. Tego jeszcze nie ogarniam.
W test_filterWrite() zawołanie mocka m, wygeneruje handler - czyli zadziała jak open(), które zwiększy licznik wywołań, dlatego można z tego skorzystać, dopiero po sprawdzeniu asercji (linijka 14).

Jeżeli potrzebujemy aby z uchwytu można było pobrać dane (przykład test_fileRead), istnieje alternatywne rozwiązanie z przesłonięciem __enter__ oraz __iter__. Nie zwiększa to licznika open i assert w linijce 27, będzie działał poprawnie.
import unittest
from unittest import mock
from unittest.mock import mock_open
import asdf

class TestFindFile(unittest.TestCase):
    def test_fileWrite(self):
        m = mock_open()
        with mock.patch('asdf.open', m, create=True):
            fileName = 'aaa.txt'
            asdf.fileWrite(fileName)

            m.assert_called_once_with(fileName, 'w+')
            file_handler = m()
            file_handler.write.assert_called_once_with('text\n')
            file_handler.close.assert_called_once_with()

    def test_fileRead(self):
        m = mock_open()
        with mock.patch('asdf.open', m, create=True):
            fileName = 'aaa.txt'
            file_handler = m.return_value.__enter__.return_value
            file_handler.__iter__.return_value = ['line1', 'line2']

            self.assertEqual(2, asdf.fileRead(fileName))

            m.assert_called_once_with(fileName, 'r')
            file_handler.close.assert_called_once_with()

29 sierpnia 2014

python "with"

Chociaż w samym języku "with" jest już od dawna, dopiero teraz się z nim mierze. with umożliwia bezpieczne zarządzanie obiektem (po opuszczeniu bloku, zwalnia zasoby, które przy wejściu były zarezerwowane). W klasycznym przykładzie z open(), with otwiera plik i zwraca uchwyt do niego (przez as), który zostanie zwolniony po opuszczeniu bloku (automatycznie zostanie zawołane f.close()).
with open("aaa.txt") as f:
    data = f.read()
Mechanizm ten można stosować, gdy obiekt, który przekażemy do with posiada metody __enter__() (która będzie wołana na wejściu do bloku, oraz metodę __exit__() (która zostanie zwołana przy wyjściu z bolku). Jeżeli podczas wykonywania zostanie rzucony jakiś wyjątek, w pierwszej kolejności zostanie zwolniony zasób, a następnie wyjątek zostanie wyrzucony wyżej. Tutaj dwa linki, które dokładniej wyjaśniają wszystkie zawiłości.

28 sierpnia 2014

[C++] RAII - Resource Acquisition Is Initialization

Notatka z wykładu Michaela Caissea "Introduction to Modern C++ Techniques" na C++Now (dawniej BoostCon), dotycząca wzorca projektowego RAII. Choć mam okazję korzystać z jej dobrodziejstw przy okazji boost-a, oraz biblioteki standardowej to jednak sam nigdy nic nie napisałem. Technika polega na przyjęciu zasobu, podczas konstruowania obiektu i zwalniania podczas jego destrukcji. Programista nie musi pamiętać o wykonaniu dodatkowych czynności, gdy zasób już nie będzie potrzebny, zwalnianiem zajmie się kompilator.
Przykład poniżej pokazuje zarządzeniem uchwytem do pliku (w konstruktorze plik jest otwierany, w destruktorze niszczony), za pomocą funkcji znanych z C.
#include <iostream>
#include <string>
#include <cstdio>

class FileManager {
public:
    FileManager(const std::string& filePath) {
        std::cout << "Open file" << std::endl;
        _fileHandler = std::fopen(filePath.c_str(), "w+");
    }

    void write(const std::string& str) {
        if(_fileHandler == nullptr)
            return;

        if(std::fputs(str.c_str(), _fileHandler) == EOF)
            throw std::exception();
    }

    ~FileManager() noexcept {
        std::cout << "Close file" << std::endl;
        if(_fileHandler != nullptr)
            std::fclose(_fileHandler);
    }

private:
    FILE* _fileHandler;
};

void modifyFile() {
    FileManager f("aaa.txt");
    f.write("some text\n");
}

int main() {
    modifyFile();
    std::cout << "End" << std::endl;
    return 0;
}
Wynik:
Open file
Close file
End

27 sierpnia 2014

[python] mockowanie iterowanych obiektów pętli

Problem, który wciąż do mnie powracał. Jak zmockować funkcję (generator), wołaną z pętli for. Ponieważ taka funkcje może zwrócić kilka wyników, cały czas próbowałem zaprząc do działania "side_effect" - bezskutecznie. Myślałem, że jej działanie jest podobne do generatora, cóż znów się czegoś nauczyłem. Pętla wymaga aby na obiekcie (z którego pobierane są dane) dało się iterować, dlatego "return_value" zwracające np. listę rozwiązuje problem. "side_effect" nadaje się bardziej do mockowania zachowań wewnątrz pętli.

Przykład z os.walk(), która zwraca trzy elementową krotkę:
import os

def findFile(fileName):
    for root, dirs, files in os.walk('~/'):
        if fileName in files:
            return os.path.join(root, fileName)

    return None
Test:
import unittest
import mock
from mock import call
import asdf

class TestFindFile(unittest.TestCase):
    @mock.patch('asdf.os')
    def test_findFIle(self, mockOs):
        fileName = 'aaa.txt'

        mockOs.walk.return_value = [('/home/user',        ('folder',),      ('bbb.txt',)),
                                    ('/home/user/folder', ('fol1', 'fol2'), (fileName,))]
        mockOs.path.join.return_value = '/home/user/folder' + fileName

        self.assertEqual('/home/user/folder' + fileName, asdf.findFile(fileName))

        mockOs.walk.assert_called_once_with('~/')
        mockOs.path.join.assert_called_once_with('/home/user/folder', fileName)

17 sierpnia 2014

8 sierpnia 2014

[C++11] nullptr

W C/C++ przyjęło się oznaczać pusty wskaźnik przez NULL, jednak identyfikator ten zostanie rozwinięty i tak do 0. Może to doprowadzić do pewnej niejednoznaczności, gdy posiadamy dwie przeciążone funkcje:
int foo(int);
int foo(char *);
Jeżeli NULL zostanie rozwinięte do 0L (ponieważ standard nie gwarantuje, że będzie to 0 - 32bity), to w takim przypadku nie mamy pewności, która metoda zostanie zawołana. Nowy standard wprowadza nowe słowo kluczowe nullptr (będące typu std::nullptr_t). Stała ta nie może być przypisywana ani porównywana z typami całkowitymi, może jednak porównywać ją z dowolnym typem wskaźnikowym.

Źródła:

7 sierpnia 2014

[Windows] Defrag Toos - sysinternals konfiguracja

Zbiór fajnych podcastów na temat diagnozowania problemów w Windowsie, za pomocą narzędzi sysinternals. Aby uzyskać lepszą diagnostykę niezbędna jest instalacja "Microsoft Windows SDK for Windows 7 and .NET Framework 4", w którym to znajduje się się lepsza wersja dbghelp.dll.
Oczywiście nie obyło się bez problemów. Po pierwsze, trzeba było odinstalować "Microsoft Visual C++ 2010 x86 Redistributable", aby w ogóle doprowadzić do instalacji (doprowadziły mnie do tego fail logi).
Po drugie nie wszystkie narzędzie z SDK rozpakowują się tam, gdzie to zostało wskazane, większość i tak wędruje do Program Files.

Interesują nas narzędzie dostępne w "Redistributable Packages" (czyli Microsoft Visual C++ 2010, Application Verifier, Debbuging Tools, Windows Performance Toolkit). Jednak SDK ściągnie jedynie instalatory. Nas interesują instalatory debbugera dla wersji 32 i 64 bitowej.
c:\Program Files\Microsoft SDKs\Windows\v7.1\Redist\Debugging Tools for Windows\dbg_amd64.msi
c:\Program Files\Microsoft SDKs\Windows\v7.1\Redist\Debugging Tools for Windows\dbg_x86.msi
Można je zainstalować w dowolnej lokalizacji. Każda posiada odpowiednią wersję dbghelp.dll, która będzie nam potrzebna - można ją nawet przekopiować na USB (diagnostyczny) z innymi narzędziami sysinternals.

Niektóre z narzędzi (jak Process Explorer) potrafią pokazywać jakie biblioteki dll, są załadowane przez dany proces do pamięci. Przydać się mogę symbole debbugjące dostępne do Microsoftu. Ścieżki, gdzie będą przechowywane, można ustawić za pomocą poniższego skryptu. Ważne są też zmienne środowiskowe (które zdaje się, są również wykonywane przez inne tego typu programy). Magiczne SRV, z tego co zrozumiałem, ustawia przeszukiwanie najpierw w lokalnym katalogu, a jeżeli ich tam nie znajdzie, ściąga symbole z serwera i robi ich backup w lokalnym katalogu (działanie magicznych gwiazdek).

Symbols.cmd:
md c:\My
md c:\My\Src
md c:\My\Sym
md c:\My\SymCache
setx /M _NT_SOURCE_PATH SRV*C:\My\Src
setx /M _NT_SYMBOL_PATH SRV*C:\My\Sym*http://msdl.microsoft.com/download/symbols
setx /M _NT_SYMCACHE_PATH C:\My\SymCache
pause

Drugi skrypt podawany przez autorów, służy do drukowania większej ilości detali (jeżeli dobrze zrozumiałem) - nie testowałem. Informacje na stronie mówią, że może to spowolnić wczytywanie symboli, więc warto włączać flagę tylko, gdy jest to uzasadnione.

DbgHelp_Logging.cmd:
rem msdn.microsoft.com/en-us/library/windows/desktop/ms680687.aspx
md c:\My
md c:\My\DbgHelp
setx DBGHELP_DBGOUT 1 
setx DBGHELP_LOG C:\My\DbgHelp\DbgHelpLog.txt
pause

6 sierpnia 2014

clang-format

clang-format.py umożliwia zmianę formatowania pliku (C++) według przyjętej w danym projekcie konwencji. Pokrótce omówił to narzędzie Chandler Carruth w swoim wykładzie na GoingNative 2013 (25 minuta):
Potrafi on korzystać z kilku wbudowanych styli: Google, WebKit, llvm. Możliwe jest także skorzystanie z własnego, za pomocą przełącznika -style=file. Styl taki zostanie zaczytany z pliku .clang-format, który musi znajdować się gdzieś w ścieżce powyżej wywołania. Własny styl można zbudować, na podstawie już istniejącego, najpierw zrzucając oryginał (--dump-config) go pliku, a następnie modyfikując.
# użyj wbudowanego stylu
clang-format-3.5 -style=WebKit -i main.cpp

# zrzuć styl Google do pliku .clang-format
clang-format-3.5 -style=Google --dump-config > .clang-format

# skorzystaj z własnego stylu
clang-format-3.5 -style=file -i main.cpp
Działanie formatera, można zautomatyzować np. w QtCreatorze. Jedna uwaga, wszelkie modyfikacje w:
"Tools | Options... | Environment | External Tools | Add | Add Tool", kasują skrót klawiaturowy, który został podpięty dla takiego formatera.

5 sierpnia 2014

[gcc + tutorial] x86 Assembly

Zobaczyłem fajny przełącznik (-masm) do gcc, który wyrzuca kod asemblera bardziej przypominający MASM niż NASM.
gcc main.cpp -S -masm=intel
Tutorial do asemblera x86:

4 sierpnia 2014