Testing Port Conflict Resolution Implementation in TheFuck
This test suite validates the port_already_in_use rule functionality in TheFuck project, focusing on handling port conflicts and process management. The tests ensure proper detection and resolution of port 8080 conflicts across different runtime environments.
Test Coverage Overview
Implementation Analysis
Technical Details
Best Practices Demonstrated
nvbn/thefuck
tests/rules/test_port_already_in_use.py
from io import BytesIO
import pytest
from thefuck.rules.port_already_in_use import match, get_new_command
from thefuck.types import Command
outputs = [
'''
DE 70% 1/1 build modulesevents.js:141
throw er; // Unhandled 'error' event
^
Error: listen EADDRINUSE 127.0.0.1:8080
at Object.exports._errnoException (util.js:873:11)
at exports._exceptionWithHostPort (util.js:896:20)
at Server._listen2 (net.js:1250:14)
at listen (net.js:1286:10)
at net.js:1395:9
at GetAddrInfoReqWrap.asyncCallback [as callback] (dns.js:64:16)
at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:83:10)
''',
'''
[6:40:01 AM] <START> Building Dependency Graph
[6:40:01 AM] <START> Crawling File System
ERROR Packager can't listen on port 8080
Most likely another process is already using this port
Run the following command to find out which process:
lsof -n -i4TCP:8080
You can either shut down the other process:
kill -9 <PID>
or run packager on different port.
''',
'''
Traceback (most recent call last):
File "/usr/lib/python3.5/runpy.py", line 184, in _run_module_as_main
"__main__", mod_spec)
File "/usr/lib/python3.5/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/home/nvbn/exp/code_view/server/code_view/main.py", line 14, in <module>
web.run_app(app)
File "/home/nvbn/.virtualenvs/code_view/lib/python3.5/site-packages/aiohttp/web.py", line 310, in run_app
backlog=backlog))
File "/usr/lib/python3.5/asyncio/base_events.py", line 373, in run_until_complete
return future.result()
File "/usr/lib/python3.5/asyncio/futures.py", line 274, in result
raise self._exception
File "/usr/lib/python3.5/asyncio/tasks.py", line 240, in _step
result = coro.send(None)
File "/usr/lib/python3.5/asyncio/base_events.py", line 953, in create_server
% (sa, err.strerror.lower()))
OSError: [Errno 98] error while attempting to bind on address ('0.0.0.0', 8080): address already in use
Task was destroyed but it is pending!
task: <Task pending coro=<RedisProtocol._reader_coroutine() running at /home/nvbn/.virtualenvs/code_view/lib/python3.5/site-packages/asyncio_redis/protocol.py:921> wait_for=<Future pending cb=[Task._wakeup()]>>
'''
]
lsof_stdout = b'''COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
node 18233 nvbn 16u IPv4 557134 0t0 TCP localhost:http-alt (LISTEN)
'''
@pytest.fixture(autouse=True)
def lsof(mocker):
patch = mocker.patch('thefuck.rules.port_already_in_use.Popen')
patch.return_value.stdout = BytesIO(lsof_stdout)
return patch
@pytest.mark.usefixtures('no_memoize')
@pytest.mark.parametrize(
'command',
[Command('./app', output) for output in outputs]
+ [Command('./app', output) for output in outputs])
def test_match(command):
assert match(command)
@pytest.mark.usefixtures('no_memoize')
@pytest.mark.parametrize('command, lsof_output', [
(Command('./app', ''), lsof_stdout),
(Command('./app', outputs[1]), b''),
(Command('./app', outputs[2]), b'')])
def test_not_match(lsof, command, lsof_output):
lsof.return_value.stdout = BytesIO(lsof_output)
assert not match(command)
@pytest.mark.parametrize(
'command',
[Command('./app', output) for output in outputs]
+ [Command('./app', output) for output in outputs])
def test_get_new_command(command):
assert get_new_command(command) == 'kill 18233 && ./app'