├── .idea └── vcs.xml ├── dojos ├── lendico │ ├── sinais.py │ └── test_sinais.py └── py-ne-2018 │ ├── sinais.py │ └── test_sinais.py ├── LICENSE ├── .gitignore └── README.md /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /dojos/lendico/sinais.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | 5 | def search(query, data): 6 | query = query.replace('-',' ') 7 | words = set(query.upper().split()) 8 | for code, char, name in data: 9 | name = name.replace('-',' ') 10 | if words <= set(name.split()): 11 | yield f'{code}\t{char}\t{name}' 12 | 13 | def reader(): 14 | with open('UnicodeData.txt') as _file: 15 | for line in _file: 16 | code, name = line.split(';')[:2] 17 | char = chr(int(code, 16)) 18 | yield f'U+{code}', char, name 19 | 20 | 21 | def main(*words): 22 | if len(words) < 1: 23 | print("Please provide one word or more", file=sys.stderr) 24 | return 25 | 26 | query = ' '.join(words) 27 | index = -1 28 | 29 | for index, line in enumerate(search(query, reader())): 30 | print(line) 31 | 32 | if index == -1: 33 | print("No results", file=sys.stderr) 34 | 35 | if __name__ == "__main__": 36 | main(*sys.argv[1:]) 37 | -------------------------------------------------------------------------------- /dojos/py-ne-2018/sinais.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | 5 | 6 | def analisar_documento(documento): 7 | res = [] 8 | for linha in documento: 9 | res.append(analisar_linha(linha)) 10 | return res 11 | 12 | 13 | def analisar_linha(linha): 14 | codigo, nome = linha.split(';')[:2] 15 | return codigo, nome 16 | 17 | 18 | def buscar(consulta, registros): 19 | achados = [] 20 | consulta = set(consulta.upper().split(' ')) 21 | for codigo, nome in registros: 22 | tokens = set(nome.split(' ')) 23 | if consulta <= tokens: 24 | achados.append((codigo, nome)) 25 | return achados 26 | 27 | 28 | def formatar(resultados): 29 | formatados = [f'U+{codigo}\t{chr(int(codigo, 16))}\t{nome}' 30 | for codigo, nome in resultados] 31 | return formatados 32 | 33 | 34 | def main(args): 35 | text_busca = ' '.join(args) 36 | with open('UnicodeData.txt') as documento: 37 | resultados = formatar(buscar(text_busca, 38 | analisar_documento(documento))) 39 | 40 | print('\n'.join(resultados)) 41 | 42 | 43 | if __name__ == '__main__': 44 | main(sys.argv[1:]) 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2018, Luciano Ramalho 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | .idea/* 3 | __pycache__/ 4 | *.py[cod] 5 | *$py.class 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *.cover 48 | .hypothesis/ 49 | .pytest_cache/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | local_settings.py 58 | db.sqlite3 59 | 60 | # Flask stuff: 61 | instance/ 62 | .webassets-cache 63 | 64 | # Scrapy stuff: 65 | .scrapy 66 | 67 | # Sphinx documentation 68 | docs/_build/ 69 | 70 | # PyBuilder 71 | target/ 72 | 73 | # Jupyter Notebook 74 | .ipynb_checkpoints 75 | 76 | # pyenv 77 | .python-version 78 | 79 | # celery beat schedule file 80 | celerybeat-schedule 81 | 82 | # SageMath parsed files 83 | *.sage.py 84 | 85 | # Environments 86 | .env 87 | .venv 88 | env/ 89 | venv/ 90 | ENV/ 91 | env.bak/ 92 | venv.bak/ 93 | 94 | # Spyder project settings 95 | .spyderproject 96 | .spyproject 97 | 98 | # Rope project settings 99 | .ropeproject 100 | 101 | # mkdocs documentation 102 | /site 103 | 104 | # mypy 105 | .mypy_cache/ 106 | -------------------------------------------------------------------------------- /dojos/lendico/test_sinais.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from sinais import reader, search, main 3 | 4 | @pytest.fixture 5 | def data(): 6 | return [ 7 | ('U+00AE', '®', 'REGISTERED SIGN'), 8 | ('U+265D', '♝', 'BLACK CHESS BISHOP'), 9 | ('U+2657', '♗', 'WHITE CHESS BISHOP') 10 | ] 11 | 12 | 13 | def test_registered(data): 14 | res = ['U+00AE\t®\tREGISTERED SIGN'] 15 | assert list(search('registered', data)) == res 16 | 17 | 18 | def test_multiple_results(data): 19 | res = ['U+265D\t♝\tBLACK CHESS BISHOP', 20 | 'U+2657\t♗\tWHITE CHESS BISHOP'] 21 | assert list(search('BISHOP', data)) == res 22 | 23 | 24 | def test_multiple_words_query(data): 25 | res = ['U+265D\t♝\tBLACK CHESS BISHOP', 26 | 'U+2657\t♗\tWHITE CHESS BISHOP'] 27 | assert list(search('BISHOP CHESS', data)) == res 28 | 29 | 30 | def test_reads_file(): 31 | result = list(reader()) 32 | assert len(result) >= 10000 33 | expected = 'U+0041', 'A', 'LATIN CAPITAL LETTER A' 34 | assert expected in result 35 | 36 | 37 | def test_main_single_result(capsys): 38 | main('REGISTERED') 39 | captured = capsys.readouterr() 40 | assert captured.out == 'U+00AE\t®\tREGISTERED SIGN\n' 41 | 42 | 43 | def test_main_multiple_results(capsys): 44 | main('CHESS', 'BLACK') 45 | captured = capsys.readouterr() 46 | assert captured.out == '\n'.join([ 47 | 'U+265A\t♚\tBLACK CHESS KING', 48 | 'U+265B\t♛\tBLACK CHESS QUEEN', 49 | 'U+265C\t♜\tBLACK CHESS ROOK', 50 | 'U+265D\t♝\tBLACK CHESS BISHOP', 51 | 'U+265E\t♞\tBLACK CHESS KNIGHT', 52 | 'U+265F\t♟\tBLACK CHESS PAWN', 53 | ]) + '\n' 54 | 55 | def test_we_dont_have_result(capsys): 56 | main('batata') 57 | captured = capsys.readouterr() 58 | assert captured.err == 'No results\n' 59 | 60 | def test_user_dont_input_anything(capsys): 61 | main() 62 | captured = capsys.readouterr() 63 | assert captured.err == 'Please provide one word or more\n' 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TDD com Pytest 2 | 3 | ## Coding Dojo 4 | 5 | * Treino coletivo de programação: prática em um ambiente seguro, como um dojo de artes marciais 6 | 7 | * Divertido e eficaz para: 8 | 9 | * Compartilhar conhecimento 10 | * Treinar boas práticas de programação (ex.: TDD) 11 | * Aprender novas linguagens ou APIs 12 | * Integrar novos membros ao time 13 | 14 | * Dojo no formato *randori*: 15 | 16 | * Duplas se sucedem resolvendo o problema com TDD 17 | 18 | * Uma dupla: piloto e co-piloto 19 | * piloto opera o teclado 20 | * co-piloto ajuda na solução 21 | * dupla conversa em voz alta 22 | 23 | * Após 5 minutos: 24 | * piloto volta para a platéia 25 | * co-piloto vira piloto 26 | * voluntário da platéia vira co-piloto 27 | 28 | * TDD: desenvolvimento guiado por testes 29 | 30 | * Escrever testes **antes** de implementar **qualquer** nova funcionalidade 31 | 32 | * Ajuda a deixar claro **o que deve ser feito** (a API) antes de mergulhar em **como** fazer 33 | 34 | * Ajuda a proporcionar uma boa DX (Developer eXperience) 35 | 36 | * Usar **baby steps** 37 | 38 | * Baby steps = passinhos de bebê 39 | 40 | * Para avançar com segurança e constância, cada passo deve ser bem simples 41 | 42 | * Resista à tentação de resolver vários casos de uma vez só, ou partir direto para generalização e abstração 43 | 44 | * Não tenha medo de implementar passos bem pequenos e triviais 45 | 46 | * Implementar o código mais simples possível que satisfaça os testes 47 | 48 | * No começo da jornada com TDD, é melhor sempre errar para menos: o menor passinho que você consegue imaginar 49 | 50 | * Com o tempo você aprende a regular o tamanho do passo de acordo com o desafio que está enfrentando a cada momento 51 | 52 | 53 | * Regras do coding dojo 54 | 55 | * Pilotos devem conversar em voz alta para a platéia poder acompanhar o raciocínio 56 | 57 | * Enquato o teste não estiver passando, a platéia não pode se manifestar (exceto a pedido dos pilotos) 58 | 59 | * Quando o teste passa, pessoas da platéia podem ajudar a decidir qual o próximo passo 60 | 61 | * Pilotos podem pedir ajuda a qualquer momento 62 | 63 | 64 | -------------------------------------------------------------------------------- /dojos/py-ne-2018/test_sinais.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import io 3 | from sinais import analisar_linha, analisar_documento, buscar 4 | from sinais import formatar, main 5 | 6 | 7 | @pytest.fixture 8 | def documento(): 9 | return io.StringIO('0041;LATIN CAPITAL LETTER A;Lu;0;L;;;;;N;;;;0061;\n' 10 | '0042;LATIN CAPITAL LETTER B;Lu;0;L;;;;;N;;;;0062;\n') 11 | 12 | 13 | def test_analisar_documento(documento): 14 | resultado = analisar_documento(documento) 15 | assert resultado == [('0041', 'LATIN CAPITAL LETTER A'), 16 | ('0042', 'LATIN CAPITAL LETTER B')] 17 | 18 | 19 | def test_analisar_linha(): 20 | linha = '0041;LATIN CAPITAL LETTER A;Lu;0;L;;;;;N;;;;0061;' 21 | codigo, nome = analisar_linha(linha) 22 | assert codigo == '0041' 23 | assert nome == 'LATIN CAPITAL LETTER A' 24 | 25 | 26 | def test_buscar_latin(documento): 27 | busca = buscar('LATIN', analisar_documento(documento)) 28 | assert busca == [('0041', 'LATIN CAPITAL LETTER A'), 29 | ('0042', 'LATIN CAPITAL LETTER B')] 30 | 31 | 32 | def test_buscar_B(documento): 33 | busca = buscar('B', analisar_documento(documento)) 34 | assert busca == [('0042', 'LATIN CAPITAL LETTER B')] 35 | 36 | 37 | def test_buscar_b(documento): 38 | busca = buscar('b', analisar_documento(documento)) 39 | assert busca == [('0042', 'LATIN CAPITAL LETTER B')] 40 | 41 | 42 | def test_buscar_a(documento): 43 | busca = buscar('a', analisar_documento(documento)) 44 | assert busca == [('0041', 'LATIN CAPITAL LETTER A')] 45 | 46 | 47 | def test_buscar_string_vazia(documento): 48 | busca = buscar('', analisar_documento(documento)) 49 | assert busca == [] 50 | 51 | 52 | def test_buscar_varias(documento): 53 | busca = buscar('B LETTER', analisar_documento(documento)) 54 | assert busca == [('0042', 'LATIN CAPITAL LETTER B')] 55 | 56 | 57 | def test_formatar_resultado(): 58 | esperado = ['U+0041\tA\tLATIN CAPITAL LETTER A'] 59 | assert esperado == formatar([('0041', 'LATIN CAPITAL LETTER A')]) 60 | 61 | 62 | def test_formatar_resutado_varios(): 63 | esperado = ['U+0041\tA\tLATIN CAPITAL LETTER A', 64 | 'U+0042\tB\tLATIN CAPITAL LETTER B'] 65 | assert esperado == formatar([('0041', 'LATIN CAPITAL LETTER A'), 66 | ('0042', 'LATIN CAPITAL LETTER B')]) 67 | 68 | 69 | def test_main(capsys): 70 | main(['registered']) 71 | captured = capsys.readouterr() 72 | assert captured.out == "U+00AE\t®\tREGISTERED SIGN\n" 73 | --------------------------------------------------------------------------------