Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/google/adk/tools/environment/_read_file_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ async def run_async(
total = len(lines_bytes)
start = max(1, start_line or 1)
end = min(total, end_line or total)
if total == 0 and start == 1 and end == 0:
return {'status': 'ok', 'content': ''}
if start > total:
return {
'status': 'error',
Expand Down
62 changes: 62 additions & 0 deletions tests/unittests/tools/environment/test_read_file_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,68 @@ async def _env(tmp_path: Path):
class TestReadFileTool:
"""Tests for ReadFileTool behavior."""

@pytest.mark.parametrize('content', [b'', b'a\nb\n'])
@pytest.mark.parametrize(
'line_range',
[
{},
{'start_line': 1},
{'start_line': 0},
{'start_line': -1},
{'end_line': 0},
{'end_line': 5},
{'start_line': 1, 'end_line': 100},
],
)
async def test_read_whole_file_with_equivalent_line_ranges(
self, env: LocalEnvironment, content: bytes, line_range: dict[str, int]
):
"""Equivalent whole-file ranges succeed for empty and nonempty files."""
await env.write_file('sample.txt', content)

result = await ReadFileTool(env).run_async(
args={'path': 'sample.txt', **line_range}, tool_context=None
)

expected = '' if not content else ' 1\ta\n 2\tb\n'
assert result == {'status': 'ok', 'content': expected}

@pytest.mark.parametrize(
'line_range', [{'end_line': 1}, {'start_line': 1, 'end_line': 1}]
)
async def test_read_empty_file_through_first_line(
self, env: LocalEnvironment, line_range: dict[str, int]
):
"""An explicit first-line boundary also succeeds for an empty file."""
await env.write_file('empty.txt', b'')

result = await ReadFileTool(env).run_async(
args={'path': 'empty.txt', **line_range}, tool_context=None
)

assert result == {'status': 'ok', 'content': ''}

@pytest.mark.parametrize(
'line_range',
[{'start_line': 2}, {'start_line': 2, 'end_line': 100}, {'end_line': -1}],
)
async def test_read_empty_file_with_line_range_returns_error(
self, env: LocalEnvironment, line_range: dict[str, int]
):
"""Invalid ranges still report that an empty file has no lines."""
await env.write_file('empty.txt', b'')
start = line_range.get('start_line', 1)

result = await ReadFileTool(env).run_async(
args={'path': 'empty.txt', **line_range}, tool_context=None
)

assert result == {
'status': 'error',
'error': f'`start_line` {start} exceeds file length (0 lines).',
'total_lines': 0,
}

@pytest.mark.asyncio
async def test_read_file_with_line_range_returns_selected_lines(
self, env: LocalEnvironment
Expand Down
Loading