OpenCyber Academy
Graduação · Capítulo 8 · Português
Código do curso: OCA-114Capítulo 08: OCA-114-C008

Capítulo 8 — Programação II

Ensino gratuito e aberto para quem deseja proteger pessoas e tecnologias. Não é preciso criar uma conta para estudar ou consultar as respostas.

Mapa da aula

Aula 08 publicada. Mapa, exemplo, EX022.

flowchart TD
  N0["8.1 Funções puras são revisáveis"]
  N1["8.2 Falhar fechado"]
  N2["8.3 Testes como documentação"]
  N3["8.4 Registrar decisões para revisão"]
  N4["Síntese"]
  N0 --> N1 --> N2 --> N3 --> N4
  N4 --> EX["Exemplo resolvido"]
  EX --> Q["Exercício EX022"]

Desenho lógico

flowchart LR
  N0["8.1 Funções puras são revisáveis"]
  N1["8.2 Falhar fechado"]
  N2["8.3 Testes como documentação"]
  N3["8.4 Registrar decisões para revisão"]
  N4["Síntese"]
  N0 --> N1 --> N2 --> N3 --> N4 --> Q["EX022"]
[8.1 Funções puras são revisáveis]
    |
    v
[8.2 Falhar fechado]
    |
    v
[8.3 Testes como documentação]
    |
    v
[8.4 Registrar decisões para revisão]
    |
    v
[Síntese]
    |
    v
[Exemplo resolvido] --> [Exercício EX022]

Objetivos de aprendizagem

Você vai (1) estruturar uma ferramenta de análise como funções puras com testes; (2) usar o unittest da biblioteca padrão; (3) fazer as funções falharem fechadas; e (4) versionar um script para que um resultado possa ser reproduzido meses depois.

8.1 Funções puras são revisáveis

Uma função que recebe entradas e devolve uma decisão, sem ler a rede nem o relógio, pode ser testada exaustivamente. Para ferramentas de TO isso importa, porque o revisor muitas vezes é um engenheiro de controle que só aceitará uma ferramenta se o seu comportamento nos casos-limite for demonstrado. Mantenha a entrada e a saída (arquivos, impressão) nas bordas e a lógica de decisão no meio. O classificador de Maya foi escrito assim: classify(code) recebe um código de função e devolve uma palavra, de modo que um engenheiro de controle consegue ler a lista de testes e ver todas as decisões sem rodar uma captura.

8.2 Falhar fechado

Quando uma função de lista de permissão recebe uma origem desconhecida, um código de função desconhecido ou um endereço malformado, ela deve devolver "bloquear" em vez de levantar uma exceção que um chamador poderia capturar e ignorar. Falhar fechado numa ferramenta de monitoramento significa "sinalizar para revisão"; num dispositivo de imposição, significa "negar". Nenhum dos dois significa "supor que está tudo bem". O primeiro classificador de Maya falhava aberto: tudo o que estava fora do conjunto de escritas devolvia "read". Um revisor perguntou sobre o código 8, diagnósticos, cujas subfunções podem reiniciar as comunicações, e a resposta expôs o defeito. A correção dela devolve "unknown", o que, num monitor offline, significa sinalizar para revisão.

8.3 Testes como documentação

Uma pequena lista de casos de unittest se lê como uma especificação:

import unittest
def classify(fc):
    return "write" if fc in (5, 6, 15, 16) else ("read" if fc in (1, 2, 3, 4) else "unknown")
class T(unittest.TestCase):
    def test_codes(self):
        self.assertEqual(classify(3), "read"); self.assertEqual(classify(16), "write")
        self.assertEqual(classify(8), "unknown"); self.assertEqual(classify(43), "unknown")
unittest.main(argv=["x"], exit=False)

Executá-lo imprime Ran 1 test e OK. O código 43 (encapsulated interface transport, usado para identificação de dispositivos) é deliberadamente "unknown" em vez de "read", porque o autor da ferramenta não raciocinou sobre ele.

A lista de testes é também a especificação que um revisor assina: classify(3) é leitura, classify(16) é escrita, classify(8) e classify(43) são desconhecidos. O defeito original de Maya teria sido pego pela única asserção de que o código 8 devolve "unknown"; essa asserção agora está na lista. Quando um novo código de função aparece numa captura da LDHP, a primeira mudança é uma nova asserção com a resposta esperada, escrita antes de o classificador ser editado.

Exemplo resolvido (LDHP sintética)

O primeiro classificador de Maya devolvia "read" para tudo o que não estava no conjunto de escritas. Um revisor perguntou o que o código 8 (diagnósticos) devolveria. A resposta — "read" — estava errada: algumas subfunções de diagnóstico reiniciam as comunicações. Ela mudou o padrão para "unknown" e acrescentou o teste acima.

Caso sintético — Lakeside District Heating Plant. O classificador é uma ferramenta didática para capturas offline, não um dispositivo de imposição.

8.4 Registrar decisões para revisão

Cada decisão que a ferramenta toma deve ser registrada com a entrada que a produziu, a regra que disparou e a versão do script, por exemplo fc=8 -> unknown (rule: default) v=3f2a91c. Um engenheiro de controle que revisa uma semana de capturas pode então filtrar por "unknown" e decidir, caso a caso, se o classificador precisa de uma nova regra. Esse registro também é o que permite mostrar, mais tarde, que um resultado surpreendente veio de uma versão conhecida do código, e não de uma edição feita depois.

Erros e limites

Os testes provam os casos que você escreveu, não os que você esqueceu. Registre a versão do script (um hash de commit do git) ao lado de todo resultado que você publicar.

Exercícios

Síntese

Ferramentas de TO revisáveis são puras, testadas, falham fechadas e são versionadas — propriedades que permitem a um engenheiro cético confiar na sua saída.

Respostas comentadas

EX022. "unknown". As subfunções do código 8 incluem reiniciar as comunicações ou zerar contadores, então rotulá-lo como "read" subestimaria o seu efeito; "unknown" obriga uma pessoa a olhar.

EX023. self.assertEqual(classify(8), "unknown") — com o código original, isso devolvia "read" e o teste falharia.

EX024. O hash de commit do git do script que produziu o resultado e um hash SHA-256 (mais a data da captura) do arquivo de entrada exato; a versão do Python é um terceiro item útil.