Frames, Byte für Byte · Abschlussprojekt
Abschlussprojekt: Projekt: Den Codec gegen den RFC testen
Dein Lern-Codec hat fünf Lektionen voller Prüfungen bestanden, aber keine dieser Prüfungen stammt von dir. Dieses Projekt schreibt die Tests, die ihn ehrlich halten: eine pytest-Suite in tests/test_frames.py, die ws_frames.py an die Bytes bindet, die RFC 6455 abdruckt, sodass jede spätere Änderung, die einen encode_frame und decode_frame erneut, und eine Suite wie diese, die Version des Kurses von dieser Datei, sagt dir dann, dass sonst nichts kaputtgegangen ist.
Was die Suite tun muss
Öffne tests/test_frames.py. Zwei Tests werden mitgeliefert. test_bit_string_reads_0x81 prüft ein Byte, und test_a_non_minimal_length_is_refused zeigt das Muster für einen Test, der einen Fehler erwartet: Er ruft decode_frame innerhalb von try auf, kehrt zurück, wenn AssertionError aus, wenn nichts ausgelöst wurde. Drei weitere sind Platzhalter, die mit assert False scheitern. Ersetze sie:
test_encoding_matches_the_rfc: der unmaskierte Text-Frame des RFC für “Hello”, sein-Frame für “Hello” und die Header seiner 256 Byte und 65536 Byte langen binären Frames, jeweils verglichen mit dem, was ws_frames.encode_framezurückgibt.test_decoding_matches_the_rfc: dieselben Bytes des RFC, anws_frames.decode_frameübergeben, mit Zusicherungen für den, finund die Payload, die zurückkommen.test_unseen_payloads_round_trip: eigene Payloads mit 0, 125, 126, 65535 und 65536 Bytes, die Grenzen der drei Längenformen, jeweils kodiert und dann dekodiert, wobei das Ergebnis mit dem Ausgangswert verglichen wird.
Zwei Regeln ergeben sich daraus, wie die Suite geprüft wird. Erstens: Behalte import ws_frames und rufe den Codec in jedem Test über das ws_frames.encode_frame(...). Check my work führt deine Tests gegen andere Versionen des Codecs aus und tauscht dabei jeweils eine fakes.reader_over in Stücken von 7 Bytes oder mehr, etwa fakes.pieces(frame, 7). reader_over kopiert seinen Puffer bei jedem Stück, also kostet ein 65536 Byte langer Frame in Ein-Byte-Stücken Zehntausende von Kopien und verlangsamt den Lauf. Check my work stoppt jeden Lauf deiner Tests, der länger als eine Sekunde dauert.
Bytes aus dem RFC
Dieses Kapitel schreibt Frames als durch Leerzeichen getrennte Hex-Paare, so wie hex(" ") sie ausgibt, und bytes.fromhex liest genau diese Form, Leerzeichen erlaubt, sodass du einen Frame aus einer Lektion in einen Test kopieren kannst, ohne ihn als Zahlen neu einzutippen. bytes.fromhex("81 05 48 65 6c 6c 6f") ist der “Hello”-Frame des RFC als bytes-
Ein Test, viele Fälle
Python in der Praxis, Kapitel 10, hat @pytest.mark.parametrize verwendet, um eine Testfunktion einmal pro Fall auszuführen. Lies den Decorator als “führe den Test darunter einmal für jedes Element dieser
@pytest.mark.parametrize(
"opcode, payload",
[(ws_frames.OP_TEXT, b"Hi"), (ws_frames.OP_BINARY, bytes(range(3)))],
)
def test_a_round_trip(opcode, payload):
...
pytest meldet jeden Fall als eigenen Test, also nennt sich ein scheiternder Fall selbst. Behalte die Payloads und die erwarteten Bytes in der Liste, als Daten. Die Listen werden gebaut, wenn die Datei importiert wird, also dürfen sie den Codec nicht aufrufen; die Aufrufe gehören in den Test.
Warum die Bytes des RFC wichtig sind
Ein Round Trip, also eine Payload zu kodieren und wieder zu dekodieren, beweist, dass Encoder und Decoder miteinander übereinstimmen. Er beweist nicht, dass sie mit jemand anderem übereinstimmen. Wenn beide die 16-Bit-Länge
Ein Codec schreibt die 16-Bit-Länge in encode_frame Little-Endian und liest sie in decode_frame Little-Endian, also stimmen die beiden miteinander überein. Welcher Test findet den Fehler?
Ausführen
Diese Lektion hat ein
python -m pytest -q
Mit der Datei im mitgelieferten Zustand meldet die Zusammenfassungszeile 3 failed, 2 passed: Die drei Platzhalter scheitern, und die beiden mitgelieferten Tests bestehen. Wenn deine drei Tests geschrieben sind, sollte jeder Test bestehen, und -q gibt einen Punkt pro Test aus. Das Demoprogramm läuft ebenfalls weiterhin:
python frames_demo.py
Klicke dann auf Check my work. Es führt zuerst ein paar Fälle aus jeder früheren Lektion gegen dein ws_frames.py aus, um sicherzugehen, dass der Codec fertig ist. Dann liest es tests/test_frames.py und führt deine Tests bis zu fünfmal aus: einmal mit einem eigenen korrekten Codec, bei dem jeder Test bestehen muss und mindestens fünf gesammelt werden müssen, und je einmal mit vier Codecs, die einen realistischen Fehler enthalten, bei denen mindestens ein Test scheitern muss. Jede Fehlermeldung nennt den Fehler, den deine Tests durchgelassen haben. Die Testnamen und Payloads wählst du selbst, und ein Test pro Fall statt parametrize ist in Ordnung.
Die Lücke, die bleibt
Jeder Frame in dieser Suite ist unmaskiert, und das ist die Grenze des Codecs. RFC 6455 verlangt, dass ein Client jeden Frame maskiert, den er sendet, also sendet kein echter Client die Frames, die du gerade getestet hast, und ein echter Server würde sie ablehnen. Kapitel 4 fügt das Maskieren hinzu und damit die Regel, wer maskiert und wer es nicht darf.
Aufgabe
Ersetze die drei Platzhalter für Lektion 6 in tests/test_frames.py durch Tests und behalte die beiden mitgelieferten Tests:
test_encoding_matches_the_rfc: der unmaskierte “Hello”-Text-des RFC, sein “Hello”- -Frame und die Header seiner 256 Byte und 65536 Byte langen binären Frames, verglichen mit dem, was ws_frames.encode_framezurückgibt;test_decoding_matches_the_rfc: dieselben Bytes des RFC, dekodiert vonws_frames.decode_frame, mit Zusicherungen für den, finund die Payload;test_unseen_payloads_round_trip: deine eigenen Payloads mit 0, 125, 126, 65535 und 65536 Bytes, kodiert, dekodiert und mit dem Original verglichen.
Verwende für die Fälle @pytest.mark.parametrize, wenn du möchtest; ein Test pro Fall ist auch in Ordnung. Behalte import ws_frames und rufe den Codec innerhalb der Tests als ws_frames.encode_frame(...) und ws_frames.decode_frame(...) auf, nie oben in der Datei. Übergib Frames über 1.000 Bytes an fakes.reader_over in Stücken von 7 Bytes oder mehr.
Führe python -m pytest -q im ws_frames.py aus und führt dann deine Tests in diesem Prozess aus: einmal mit einem eigenen korrekten Codec, bei dem mindestens fünf Tests gesammelt werden und alle bestehen müssen, und je einmal mit vier Codecs, die jeweils einen realistischen Fehler enthalten, bei denen mindestens ein Test scheitern muss. Ein Lauf, der länger als 1 Sekunde dauert, wird gestoppt. Die Prüfung liest dein Terminal nicht.