Een gesprek op loopback · oefening
Het gesprek testen zonder poort
Je hebt je server in Run zien echoën, sluiten en falen, en Submit heeft elke tests/test_loopback.py die je echte server op een echte
Twee sockets, geen poort
Networks with Python hoofdstuk 4 testte een client met socket.socketpair():
client_end, server_end = socket.socketpair()
Het geeft twee sockets terug die al met elkaar verbonden zijn, binnen één programma. Bytes die in het ene uiteinde gaan, komen uit het andere, en als het ene uiteinde sluit, geeft recv op het andere b"" terug. Er is geen adres om aan te binden, geen poort om te kiezen, en niets anders op de machine kan een van beide uiteinden bereiken. Voor een server_end aan serve_connection kan geven precies zoals de bedrading er een geaccepteerde verbinding aan geeft, en de client kan spelen op client_end.
Wat geeft socket.socketpair() een test?
De twee hulpfuncties
Open tests/test_loopback.py. Boven de drie plaatshoudertests staan twee meegeleverde hulpfuncties.
fixed(n) geeft bytes(range(n)) terug, bekende bytes in plaats van willekeurige. Geef het mee als random_bytes=fixed en de handshakesleutel en elke
start_server(server_end) draait loopback_demo.serve_connection(server_end) op een thread en geeft een "thread", de thread, en "outcome", een dict waarin de thread het resultaat van serve_connection onder "result" opslaat als hij klaar is. Voordat het de thread start, zet het een time-out van 1 seconde op server_end, en het start de thread met daemon=True, zoals het project van hoofdstuk 2 deed, zodat een thread die nog draait de testrun nooit in leven kan houden. Het uiteinde van de server wordt gesloten door een with-blok binnen de thread.
Drie tests
Vervang de drie plaatshouders:
test_client_and_server_agree: draailoopback_demo.client_connectionopclient_endmet twee of drie eigen teksten enrandom_bytes=fixed, en join daarna de serverthread. Assert dat het resultaat van de client je teksten als echo’s heeft en de code 1000, en dat het resultaat van de server je berichten telt met de code 1000.test_server_answers_a_ping_with_the_same_payload: doe devan de client zelf met ws_handshake.client_handshake, maak eenread_exactmetloopback_demo.make_read_exact(reader), stuur een gemaskeerdemet een eigen payload ( ws_frames.encode_framemetmask_key=fixed(4)), en lees het antwoord metws_frames.read_message(read_exact, role="client"). Assert dat het een pong met je payload is. Beëindig daarna het gesprek: stuur een gemaskeerde close 1000 en lees de beantwoordende close van de server.test_server_fails_an_unmasked_frame: stuur na de handshakews_frames.encode_frame(ws_frames.OP_TEXT, "no mask"), dat geen masker heeft, lees het antwoord, en assert dat het een close is met de code 1002. Join de thread en assert dat het resultaat van de server ook de code 1002 heeft.
Elke test begint op dezelfde manier:
client_end, server_end = socket.socketpair()
client_end.settimeout(1.0)
server = start_server(server_end)
Daarna gaat de kant van de client binnen with client_end:, zodat het uiteinde van de client gesloten wordt, ook als een assert halverwege faalt, en na dat blok komt server["thread"].join(timeout=1.0). Assert het resultaat van de server na de join: de thread slaat het pas op als hij klaar is.
Vier regels komen voort uit hoe je tests worden gecontroleerd:
Geef
client_endeen time-out van 1 seconde, en join elke thread mettimeout=1.0.Beëindig elk gesprek: de client sluit, of leest de close van de server, in plaats van een time-out uit te zitten. De time-outs zijn een vangnet voor een kapotte server; een werkende server antwoordt meteen.
Roep de functies van de server aan via de
, als loopback_demo.client_connection(...)enws_frames.read_message(...), binnen elke test. Check my work zet andere versies van de functies van de server inloopback_demoom te zien of je tests het merken; een naam die geïmporteerd is metfrom loopback_demo importzou het origineel houden.Assert codes, echo’s en payloads, niet de formulering van een reden die de server zelf schrijft, zoals de reden voor een gebroken regel, die een andere correcte server anders kan formuleren.
Waarom zet elke test een time-out van 1 seconde en joint hij de serverthread met een time-out?
Dit zijn echte sockets en een echte thread. Je tests maken ze elke keer dat ze draaien, hier in de
Draai ze
Deze les heeft een terminal. Draai vanuit de projectmap:
python -m pytest -q
Met de plaatshouders nog op hun plek meldt de samenvattingsregel 3 failed. Als je tests geschreven zijn, meldt hij 3 passed. De demo draait ook nog steeds vanuit de terminal:
python loopback_demo.py
Druk daarna op Check my work. Het leest tests/test_loopback.py en draait je tests tot vijf keer in één proces: één keer tegen een eigen werkende server, waarbij minstens drie tests verzameld moeten worden en alle moeten slagen, en één keer elk tegen vier servers met elk één fout, waarbij minstens één test moet falen. De vier fouten zijn echo’s die gemaskeerd zijn, elke close beantwoord met 1002, een pong die de payload van de ping laat vallen, en een protocolfout beantwoord met 1000. Elke run die langer dan 2 seconden duurt, wordt gestopt, en daarom moet elke test zijn gesprek beëindigen.
Je server is nu bewezen door eigen tests. De laatste les van dit hoofdstuk maakt van loopback_demo.py een commando dat meerdere clients na elkaar bedient, binnen een deadline.
Opdracht
Vervang de drie plaatshouders in tests/test_loopback.py door tests, en houd de meegeleverde fixed en start_server:
test_client_and_server_agree:loopback_demo.client_connectionop het uiteinde van de client met eigen teksten enrandom_bytes=fixed; assert de echo’s en de code 1000 aan de kant van de client, en het aantal berichten en de code 1000 in het resultaat van de server.test_server_answers_a_ping_with_the_same_payload: stuur naws_handshake.client_handshakeeen gemaskeerdemet een eigen payload en assert dat het antwoord een pong met dezelfde payload is; stuur daarna een gemaskeerde close 1000 en lees de beantwoordende close van de server. test_server_fails_an_unmasked_frame: stuur na deeen niet-gemaskeerd tekstframe en assert dat de server antwoordt met een close met 1002 en 1002 meldt in zijn resultaat.
In elke test: maak het paar met socket.socketpair(), geef het uiteinde van de client settimeout(1.0), start de server met start_server, sluit het uiteinde van de client met with, en join de thread met timeout=1.0 voordat je het resultaat van de server assert. Beëindig elk gesprek met een close in plaats van een time-out uit te zitten. Houd import loopback_demo en roep de loopback_demo.client_connection(...) enzovoort binnen de tests, nooit met from loopback_demo import.
Draai python -m pytest -q in de