Frames, byte voor byte · eindproject
Eindproject: Project: test de codec tegen de RFC
Je leercodec doorstond vijf lessen aan controles, maar geen van die controles is van jou. Dit project schrijft de tests die hem eerlijk houden: een pytest-suite in tests/test_frames.py die ws_frames.py vastpint op de bytes die RFC 6455 afdrukt, zodat elke latere wijziging die een encode_frame en decode_frame opnieuw, en een suite zoals deze, de versie van de cursus van dit bestand, is wat je vertelt dat er verder niets kapot is gegaan.
Wat de suite moet doen
Open tests/test_frames.py. Er worden twee tests meegeleverd. test_bit_string_reads_0x81 controleert één byte, en test_a_non_minimal_length_is_refused toont het patroon voor een test die een fout verwacht: hij roept decode_frame aan binnen try, keert terug als AssertionError op als er niets werd opgeworpen. Nog drie zijn plaatshouders die falen met assert False. Vervang ze:
test_encoding_matches_the_rfc: het niet-gemaskeerde tekstframe van de RFC voor “Hello”, het pingframe voor “Hello”, en de headers van de binaire frames van 256 en 65536 bytes, elk vergeleken met watws_frames.encode_frameteruggeeft.test_decoding_matches_the_rfc: dezelfde RFC-bytes gegeven aanws_frames.decode_frame, met asserts op de, finen de payload die terugkomen.test_unseen_payloads_round_trip: eigen payloads van 0, 125, 126, 65535 en 65536 bytes, de grenzen van de drie lengtevormen, elk gecodeerd en daarna gedecodeerd, met het resultaat vergeleken met waarmee je begon.
Twee regels komen voort uit hoe de suite wordt gecontroleerd. Ten eerste: houd import ws_frames en roep de codec aan via de ws_frames.encode_frame(...), binnen elke test. Check my work draait je tests tegen andere versies van de codec, waarbij telkens één fakes.reader_over in stukken van 7 bytes of meer, zoals fakes.pieces(frame, 7). reader_over kopieert zijn buffer voor elk stuk, dus een frame van 65536 bytes in stukken van één byte kost tienduizenden kopieën en vertraagt de run. Check my work stopt elke run van je tests die langer dan een seconde duurt.
Bytes uit de RFC
Dit hoofdstuk schrijft frames als hexparen gescheiden door spaties, zoals hex(" ") ze afdrukt, en bytes.fromhex leest precies die vorm, spaties toegestaan, zodat je een frame uit een les in een test kunt kopiëren zonder het opnieuw als getallen te typen. bytes.fromhex("81 05 48 65 6c 6c 6f") is het “Hello”-frame van de RFC als bytes-
Eén test, veel gevallen
Python in Practice hoofdstuk 10 gebruikte @pytest.mark.parametrize om één testfunctie één keer per geval te draaien. Lees de decorator als “draai de test hieronder één keer voor elk item in deze
@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 meldt elk geval als een eigen test, dus een falend geval noemt zichzelf. Houd de payloads en verwachte bytes in de lijst, als data. De lijsten worden gebouwd als het bestand wordt geïmporteerd, dus ze mogen de codec niet aanroepen; de aanroepen horen binnen de test.
Waarom de bytes van de RFC ertoe doen
Een round trip, een payload coderen en weer decoderen, bewijst dat de encoder en de decoder het met elkaar eens zijn. Het bewijst niet dat ze het met iemand anders eens zijn. Als beide de lengte van 16 bits
Een codec schrijft de lengte van 16 bits little-endian in encode_frame en leest haar little-endian in decode_frame, dus de twee zijn het met elkaar eens. Welke test vangt het?
Draai het
Deze les heeft een
python -m pytest -q
Met het bestand zoals het is meegeleverd, meldt de samenvattingsregel 3 failed, 2 passed: de drie plaatshouders falen, en de twee meegeleverde tests slagen. Als je drie tests geschreven zijn, moet elke test slagen, en -q drukt één punt per test af. Het demoprogramma draait ook nog steeds:
python frames_demo.py
Druk daarna op Check my work. Het draait eerst een paar gevallen uit elke eerdere les tegen je ws_frames.py, om zeker te weten dat de codec af is. Daarna leest het tests/test_frames.py, en draait het je tests tot vijf keer: één keer met een eigen correcte codec, waarbij elke test moet slagen en er minstens vijf verzameld moeten worden, en één keer elk met vier codecs die één realistische fout bevatten, waarbij minstens één test moet falen. Elke foutmelding noemt de fout die je tests doorlieten. De testnamen en payloads kies je zelf, en één test per geval in plaats van parametrize is prima.
Het gat dat overblijft
Elk frame in deze suite is niet gemaskeerd, en dat is de grens van de codec. RFC 6455 eist dat een client elk frame dat hij stuurt maskeert, dus geen echte client stuurt de frames die je net hebt getest, en een echte server zou ze weigeren. Hoofdstuk 4 voegt maskeren toe, en daarmee de regel over wie maskeert en wie dat niet mag.
Opdracht
Vervang de drie plaatshouders van les 6 in tests/test_frames.py door tests, en houd de twee meegeleverde tests:
test_encoding_matches_the_rfc: het niet-gemaskeerde “Hello”-tekstframe van de RFC, het “Hello”-pingframe, en de headers van de binairevan 256 en 65536 bytes, vergeleken met wat ws_frames.encode_frameteruggeeft;test_decoding_matches_the_rfc: dezelfde RFC-bytes gedecodeerd doorws_frames.decode_frame, met asserts op de, finen de payload;test_unseen_payloads_round_trip: je eigen payloads van 0, 125, 126, 65535 en 65536 bytes, gecodeerd, gedecodeerd, en vergeleken met het origineel.
Gebruik @pytest.mark.parametrize voor de gevallen als je wilt; één test per geval is ook goed. Houd import ws_frames en roep de codec aan als ws_frames.encode_frame(...) en ws_frames.decode_frame(...) binnen de tests, nooit bovenaan het bestand. Geef frames van meer dan 1.000 bytes aan fakes.reader_over in stukken van 7 bytes of meer.
Draai python -m pytest -q in de ws_frames.py, en draait daarna je tests in dit proces: één keer met een eigen correcte codec, waarbij minstens vijf tests verzameld moeten worden en alle moeten slagen, en één keer elk met vier codecs die elk één realistische fout bevatten, waarbij minstens één test moet falen. Een run die langer dan 1 seconde duurt, wordt gestopt. De controle leest je terminal niet.