Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

gh-131591: Add tests for _PdbClient#132976

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
pablogsal merged 8 commits intopython:mainfromgodlygeek:remote_pdb_client_tests
Apr 30, 2025

Conversation

godlygeek
Copy link
Contributor

@godlygeekgodlygeek commentedApr 25, 2025
edited by bedevere-appbot
Loading

As promised, here are some additional tests for _PdbClient. These exercise all of its features quite well, with the exception of its ability to send interrupts to the remote process. I've left that out for now since that's still a bit in flux due to#132975.

CC:@gaogaotiantian@pablogsal

@godlygeek
Copy link
ContributorAuthor

This PR needs a skip-news label.

Copy link
Member

@gaogaotiantiangaogaotiantian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Okay here's our goal - making the unittest easy to understand and easy to write. Ideally, we only needdata in our unittest, nologic at all. All the logic should be dealt with in our utility method.

For example, the test would look like

deftest_handling_info_message(self):server_messages= [            {"message":"Some message or other.\n","type":"info"},        ]stdout,stderr=self.do_test(server_messages=server_messages,outgoing_messages=[],write_success=True,# this could be default        )self.assertEqual("*** Some message or other.\n",stdout)

The test write should not need to know anything about mock, client or socket file.

We need to do something clever with the prompts and completions but the direction should be like this. We should be able to write a test with only input and output in our mind, not all the mock details.

The readline mock piece was specifically difficult to read. I think we should be able to make the unittest itself looks like - I'll press here, let's see the result.

Another suggestion is that we should replace all theblah to something a little bit more meaningful. That would also help understand the test.

I think my major goal is to make it as easy as possible for future developers to come up with their own test when they try to fix a bug. We should hide all the mock details if possible.

@godlygeek
Copy link
ContributorAuthor

godlygeek commentedApr 28, 2025
edited
Loading

Okay here's our goal - making the unittest easy to understand and easy to write. Ideally, we only needdata in our unittest, nologic at all. All the logic should be dealt with in our utility method.

I don't think I understand what you're proposing. Your example has a test setup (theserver_messages = ...), an execution of the system under test (theself.do_test), and some assertions about the observed behavior of the system under test. Those exactly correspond to the given/when/then structure of all of these tests.

Taking one of my tests, chosen mostly at random:

deftest_handling_pdb_prompts(self):"""Test responding to pdb's normal prompts."""# GIVENprompts_and_inputs= [            ("(Pdb) ","blah ["),            ("...   ","blah ]"),            ("(Pdb) ",""),            ("(Pdb) ","b ["),            ("(Pdb) ","! b ["),            ("...   ","b ]"),        ]prompts= [pi[0]forpiinprompts_and_inputs]inputs= [pi[1]forpiinprompts_and_inputs]num_prompt_msgs=sum(pi[0]=="(Pdb) "forpiinprompts_and_inputs)messages= [            {"command_list": ["b"]},        ]+ [{"prompt":"(Pdb) ","state":"pdb"}]*num_prompt_msgssockfile=MockDebuggerSocket(messages)client=self.create_pdb_client(sockfile)# WHENwith (unittest.mock.patch("pdb.input",side_effect=inputs)asinput_mock,        ):client.cmdloop()# THENexpected_outgoing= [            {"reply":"blah [\nblah ]"},            {"reply":""},            {"reply":"b ["},            {"reply":"!b [\nb ]"},        ]self.assertEqual(sockfile.outgoing,expected_outgoing)self.assertFalse(client.write_failed)self.assertEqual(client.state,"pdb")input_mock.assert_has_calls([unittest.mock.call(p)forpinprompts])

How would you prefer this test look? It's got two streams of input being fed to the system under test (theprompt messages coming from the PDB server and the user input on stdin), and it's testing that when the user's input is:

"blah [\nblah ]\n\nb [\n! b [\nb ]"
  • that it consumes 4prompt messages from the server
  • that it makes 6input() calls total (the 4(Pdb) prompts the server asked for plus 2... continuation prompts)
  • that theblah [ line and the! b [ line lead to a continuation prompt
  • that theblah ] andb ] lines complete the command started on the previous line and don't lead to another continuation prompt
  • that the empty input line line and theb [ line are submitted to the server directly without starting a continuation line

Can you show how you would set up the test to provide that input and make those assertions?

@gaogaotiantian
Copy link
Member

I would expect something like

# not sure if message is the perfect variable name but that's irrelevantmessages= [    ("server", {"command_list": ["b"]}),    ("server", {"prompt":"(Pdb) ","state":"pdb"}),    ("client", {"prompt":"(Pdb) ","input":"blah ["}),    ("client", {"prompt":"... ","input":"blah ]"}),    ("server", {"prompt":"(Pdb) ","state":"pdb"}),    ...]self.do_test(messages,expected_outgoing=expected_outgoing, ...)

In reality, we actually feed the mock server (sockfile) all the messages and just do the loop. However, this order thing gives us the possibility to check the actual message order, and gives a better idea of what's really going on.

Notice that - we don't have to checkprompt on the client side for every single test, it could be omitted so the test case would be simpler.

# not sure if message is the perfect variable name but that's irrelevantmessages= [    ("server", {"command_list": ["b"]}),    ("server", {"prompt":"(Pdb) ","state":"pdb"}),    ("client", {"input":"blah ["}),    ("client", {"input":"blah ]"}),    ("server", {"prompt":"(Pdb) ","state":"pdb"}),    ...]self.do_test(messages,expected_outgoing=expected_outgoing, ...)

What I want is 0 logic in the test case itself. I can just translate the events I expected to some data structure and I got my test case.

@godlygeek
Copy link
ContributorAuthor

What I want is 0 logic in the test case itself. I can just translate the events I expected to some data structure and I got my test case.

OK, I've pushed a new commit that does this, please take another look.

@godlygeek
Copy link
ContributorAuthor

No idea what the failure in the tsan test is about, but it seems unrelated to my changes.

Copy link
Member

@gaogaotiantiangaogaotiantian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I think overall, this is very close to what I imagine. We put all the logic in a utility function and we have only data in our unittests. I do have a few comments about the structure and details, but the direction is definitely what I want.

@pablogsalpablogsal merged commit5154d41 intopython:mainApr 30, 2025
42 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@gaogaotiantiangaogaotiantiangaogaotiantian approved these changes

Assignees
No one assigned
Labels
skip newstestsTests in the Lib/test dir
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

4 participants
@godlygeek@gaogaotiantian@picnixz@pablogsal

[8]ページ先頭

©2009-2025 Movatter.jp