Spaces:
Build error
Build error
File size: 12,958 Bytes
51ff9e5 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 |
"""Tests for the command helper functions in function_calling.py."""
import os
import pytest
from conftest import (
_close_test_runtime,
_load_runtime,
)
from openhands.agenthub.readonly_agent.function_calling import (
glob_to_cmdrun,
grep_to_cmdrun,
)
from openhands.core.logger import openhands_logger as logger
from openhands.events.action import CmdRunAction
from openhands.events.observation import CmdOutputObservation, ErrorObservation
# Skip all tests in this file if running with CLIRuntime,
# as they depend on `rg` (ripgrep) which is not guaranteed to be available.
# The underlying ReadOnlyAgent tools (GrepTool, GlobTool) also currently depend on `rg`.
# TODO: implement a fallback version of these tools that uses `find` and `grep`.
pytestmark = pytest.mark.skipif(
os.environ.get('TEST_RUNTIME') == 'cli',
reason="CLIRuntime: ReadOnlyAgent's GrepTool/GlobTool tests require `rg` (ripgrep), which may not be installed.",
)
def _run_cmd_action(runtime, custom_command: str):
action = CmdRunAction(command=custom_command)
logger.info(action, extra={'msg_type': 'ACTION'})
obs = runtime.run_action(action)
assert isinstance(obs, (CmdOutputObservation, ErrorObservation))
logger.info(obs, extra={'msg_type': 'OBSERVATION'})
return obs
def test_grep_to_cmdrun_basic():
"""Test basic pattern with no special characters."""
cmd = grep_to_cmdrun('function', 'src')
assert 'rg -li function' in cmd
assert 'Below are the execution results' in cmd
# With include parameter
cmd = grep_to_cmdrun('error', 'src', '*.js')
assert 'rg -li error' in cmd
assert "--glob '*.js'" in cmd
assert 'Below are the execution results' in cmd
def test_grep_to_cmdrun_quotes(temp_dir, runtime_cls, run_as_openhands):
"""Test patterns with different types of quotes."""
runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands)
try:
# Double quotes in pattern
cmd = grep_to_cmdrun(r'const message = "Hello"', '/workspace')
assert 'rg -li' in cmd
# Verify command works by executing it on a test file
setup_cmd = 'echo \'const message = "Hello";\' > /workspace/test_quotes.js'
obs = _run_cmd_action(runtime, setup_cmd)
assert obs.exit_code == 0
obs = _run_cmd_action(runtime, cmd)
assert obs.exit_code == 0
assert '/workspace/test_quotes.js' in obs.content
# Single quotes in pattern
cmd = grep_to_cmdrun("function\\('test'\\)", '/workspace')
assert 'rg -li' in cmd
setup_cmd = 'echo "function(\'test\') {}" > /workspace/test_quotes2.js'
obs = _run_cmd_action(runtime, setup_cmd)
assert obs.exit_code == 0
obs = _run_cmd_action(runtime, cmd)
assert obs.exit_code == 0
assert '/workspace/test_quotes2.js' in obs.content
finally:
_close_test_runtime(runtime)
def test_grep_to_cmdrun_special_chars(runtime_cls, run_as_openhands, temp_dir):
"""Test patterns with special shell characters."""
runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands)
try:
# Create test directory and files with special pattern content
setup_cmd = """
mkdir -p /workspace/test_special_patterns && \
echo "testing x && y || z pattern" > /workspace/test_special_patterns/logical.txt && \
echo "function() { return x; }" > /workspace/test_special_patterns/function.txt && \
echo "using \\$variable here" > /workspace/test_special_patterns/dollar.txt && \
echo "using \\`backticks\\` here" > /workspace/test_special_patterns/backticks.txt && \
echo "line with \\n newline chars" > /workspace/test_special_patterns/newline.txt && \
echo "matching *.js wildcard" > /workspace/test_special_patterns/wildcard.txt && \
echo "testing x > y redirection" > /workspace/test_special_patterns/redirect.txt && \
echo "testing a | b pipe" > /workspace/test_special_patterns/pipe.txt && \
echo "line with #comment" > /workspace/test_special_patterns/comment.txt && \
echo "CSS \\!important rule" > /workspace/test_special_patterns/bang.txt
"""
obs = _run_cmd_action(runtime, setup_cmd)
assert obs.exit_code == 0, 'Failed to set up test files'
special_patterns = [
r'x && y \|\| z', # Shell logical operators (escaping pipe)
r'function\(\) \{ return x; \}', # Properly escaped braces and parentheses
r'\$variable', # Dollar sign
# r"`backticks`", # Backticks
r'\\n newline', # Escaped characters
r'\*\.js', # Wildcards (escaped)
r'x > y', # Redirection
r'a \| b', # Pipe (escaped)
r'#comment', # Hash
# r"!important", # Bang
]
for pattern in special_patterns:
# Generate the grep command using our helper function
cmd = grep_to_cmdrun(pattern, '/workspace/test_special_patterns')
assert 'rg -li' in cmd
assert 'Below are the execution results of the search command:' in cmd
# Execute the command
obs = _run_cmd_action(runtime, cmd)
# Verify the command executed successfully
assert 'command not found' not in obs.content
assert 'syntax error' not in obs.content
assert 'unexpected' not in obs.content
# Check that the pattern was found in the appropriate file
if '&&' in pattern:
assert 'logical.txt' in obs.content
elif 'function' in pattern:
assert 'function.txt' in obs.content
elif '$variable' in pattern:
assert 'dollar.txt' in obs.content
# elif "backticks" in pattern:
# assert "backticks.txt" in obs.content
elif '\\n newline' in pattern:
assert 'newline.txt' in obs.content
elif '*' in pattern:
assert 'wildcard.txt' in obs.content
elif '>' in pattern:
assert 'redirect.txt' in obs.content
elif '|' in pattern:
assert 'pipe.txt' in obs.content
elif '#comment' in pattern:
assert 'comment.txt' in obs.content
# elif "!important" in pattern:
# assert "bang.txt" in obs.content
finally:
_close_test_runtime(runtime)
def test_grep_to_cmdrun_paths_with_spaces(runtime_cls, run_as_openhands, temp_dir):
"""Test paths with spaces and special characters."""
runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands)
try:
# Create test files with content in paths with spaces
setup_cmd = """
mkdir -p "src/my project" "test files/unit tests" "src/special$chars" "path with spaces and $pecial ch@rs" && \
echo "function searchablePattern() { return true; }" > "src/my project/test.js" && \
echo "function testFunction() { return 42; }" > "test files/unit tests/test.js" && \
echo "function specialFunction() { return null; }" > "src/special$chars/test.js" && \
echo "function weirdFunction() { return []; }" > "path with spaces and $pecial ch@rs/test.js"
"""
obs = _run_cmd_action(runtime, setup_cmd)
assert obs.exit_code == 0, 'Failed to set up test files'
special_paths = [
'src/my project',
'test files/unit tests',
]
for path in special_paths:
# Generate grep command and execute it
cmd = grep_to_cmdrun('function', path)
assert 'rg -li' in cmd
obs = _run_cmd_action(runtime, cmd)
assert obs.exit_code == 0, f'Grep command failed for path: {path}'
assert 'function' in obs.content, (
f'Expected pattern not found in output for path: {path}'
)
# Verify the actual file was found
if path == 'src/my project':
assert 'src/my project/test.js' in obs.content
elif path == 'test files/unit tests':
assert 'test files/unit tests/test.js' in obs.content
finally:
_close_test_runtime(runtime)
def test_glob_to_cmdrun_basic():
"""Test basic glob patterns."""
cmd = glob_to_cmdrun('*.js', 'src')
assert "rg --files src -g '*.js'" in cmd
assert 'head -n 100' in cmd
assert 'echo "Below are the execution results of the glob command:' in cmd
# Default path
cmd = glob_to_cmdrun('*.py')
assert "rg --files . -g '*.py'" in cmd
assert 'head -n 100' in cmd
assert 'echo "Below are the execution results of the glob command:' in cmd
def test_glob_to_cmdrun_special_patterns(runtime_cls, run_as_openhands, temp_dir):
"""Test glob patterns with special characters."""
runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands)
try:
# Create test files matching the patterns we'll test
setup_cmd = r"""
mkdir -p src/components src/utils && \
touch src/file1.js src/file2.js src/file9.js && \
touch src/components/comp.jsx src/components/comp.tsx && \
touch src/$special-file.js && \
touch src/temp1.js src/temp2.js && \
touch src/file.js src/file.ts src/file.jsx && \
touch "src/weird\`file\`.js" && \
touch "src/file with spaces.js"
"""
obs = _run_cmd_action(runtime, setup_cmd)
assert obs.exit_code == 0, 'Failed to set up test files'
special_patterns = [
'**/*.js', # Double glob
'**/{*.jsx,*.tsx}', # Braces
'file[0-9].js', # Character class
'temp?.js', # Single character wildcard
'file.{js,ts,jsx}', # Multiple extensions
'file with spaces.js', # Spaces
]
for pattern in special_patterns:
cmd = glob_to_cmdrun(pattern, 'src')
logger.info(f'Command: {cmd}')
# Execute the command
obs = _run_cmd_action(runtime, cmd)
assert obs.exit_code == 0, f'Glob command failed for pattern: {pattern}'
# Verify expected files are found
if pattern == '**/*.js':
assert 'file1.js' in obs.content
assert 'file2.js' in obs.content
elif pattern == '**/{*.jsx,*.tsx}':
assert 'comp.jsx' in obs.content
assert 'comp.tsx' in obs.content
elif pattern == 'file[0-9].js':
assert 'file1.js' in obs.content
assert 'file2.js' in obs.content
assert 'file9.js' in obs.content
elif pattern == 'temp?.js':
assert 'temp1.js' in obs.content
assert 'temp2.js' in obs.content
elif pattern == 'file.{js,ts,jsx}':
assert 'file.js' in obs.content
assert 'file.ts' in obs.content
assert 'file.jsx' in obs.content
elif pattern == 'file with spaces.js':
assert 'file with spaces.js' in obs.content
finally:
_close_test_runtime(runtime)
def test_glob_to_cmdrun_paths_with_spaces(runtime_cls, run_as_openhands, temp_dir):
"""Test paths with spaces and special characters for glob command."""
runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands)
try:
# Create test directories with spaces and special characters
setup_cmd = """
mkdir -p "project files/src" "test results/unit tests" "weird$path/code" "path with spaces and $pecial ch@rs" && \
touch "project files/src/file1.js" "project files/src/file2.js" && \
touch "test results/unit tests/test1.js" "test results/unit tests/test2.js" && \
touch "weird$path/code/weird1.js" "weird$path/code/weird2.js" && \
touch "path with spaces and $pecial ch@rs/special1.js" "path with spaces and $pecial ch@rs/special2.js"
"""
obs = _run_cmd_action(runtime, setup_cmd)
assert obs.exit_code == 0, 'Failed to set up test files'
special_paths = [
'project files/src',
'test results/unit tests',
]
for path in special_paths:
cmd = glob_to_cmdrun('*.js', path)
# Execute the command
obs = _run_cmd_action(runtime, cmd)
assert obs.exit_code == 0, f'Glob command failed for path: {path}'
# Verify expected files are found in each path
if path == 'project files/src':
assert 'file1.js' in obs.content
assert 'file2.js' in obs.content
elif path == 'test results/unit tests':
assert 'test1.js' in obs.content
assert 'test2.js' in obs.content
finally:
_close_test_runtime(runtime)
|