feat: Add .find() method to UICollection and EntityCollection
Implements name-based search for UI elements and entities:
- Exact match returns single element or None
- Wildcard patterns (prefix*, *suffix, *contains*) return list
- Recursive search for nested Frame children (UICollection only)
API:
ui.find("player_frame") # exact match
ui.find("enemy*") # starts with
ui.find("*_button", recursive=True) # recursive search
grid.entities.find("*goblin*") # entity search
Closes #41, closes #40
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
51e96c0c6b
commit
deb5d81ab6
|
|
@ -905,12 +905,158 @@ PyObject* UICollection::count(PyUICollectionObject* self, PyObject* value) {
|
|||
return PyLong_FromSsize_t(count);
|
||||
}
|
||||
|
||||
// Helper function to match names with optional wildcard support
|
||||
static bool matchName(const std::string& name, const std::string& pattern) {
|
||||
// Check for wildcard pattern
|
||||
if (pattern.find('*') != std::string::npos) {
|
||||
// Simple wildcard matching: only support * at start, end, or both
|
||||
if (pattern == "*") {
|
||||
return true; // Match everything
|
||||
} else if (pattern.front() == '*' && pattern.back() == '*' && pattern.length() > 2) {
|
||||
// *substring* - contains match
|
||||
std::string substring = pattern.substr(1, pattern.length() - 2);
|
||||
return name.find(substring) != std::string::npos;
|
||||
} else if (pattern.front() == '*') {
|
||||
// *suffix - ends with
|
||||
std::string suffix = pattern.substr(1);
|
||||
return name.length() >= suffix.length() &&
|
||||
name.compare(name.length() - suffix.length(), suffix.length(), suffix) == 0;
|
||||
} else if (pattern.back() == '*') {
|
||||
// prefix* - starts with
|
||||
std::string prefix = pattern.substr(0, pattern.length() - 1);
|
||||
return name.compare(0, prefix.length(), prefix) == 0;
|
||||
}
|
||||
// For more complex patterns, fall back to exact match
|
||||
return name == pattern;
|
||||
}
|
||||
// Exact match
|
||||
return name == pattern;
|
||||
}
|
||||
|
||||
PyObject* UICollection::find(PyUICollectionObject* self, PyObject* args, PyObject* kwds) {
|
||||
const char* name = nullptr;
|
||||
int recursive = 0;
|
||||
|
||||
static const char* kwlist[] = {"name", "recursive", NULL};
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|p", const_cast<char**>(kwlist),
|
||||
&name, &recursive)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
auto vec = self->data.get();
|
||||
if (!vec) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Collection data is null");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::string pattern(name);
|
||||
bool has_wildcard = (pattern.find('*') != std::string::npos);
|
||||
|
||||
if (has_wildcard) {
|
||||
// Return list of all matches
|
||||
PyObject* results = PyList_New(0);
|
||||
if (!results) return NULL;
|
||||
|
||||
for (auto& drawable : *vec) {
|
||||
if (matchName(drawable->name, pattern)) {
|
||||
PyObject* py_drawable = convertDrawableToPython(drawable);
|
||||
if (!py_drawable) {
|
||||
Py_DECREF(results);
|
||||
return NULL;
|
||||
}
|
||||
if (PyList_Append(results, py_drawable) < 0) {
|
||||
Py_DECREF(py_drawable);
|
||||
Py_DECREF(results);
|
||||
return NULL;
|
||||
}
|
||||
Py_DECREF(py_drawable); // PyList_Append increfs
|
||||
}
|
||||
|
||||
// Recursive search into Frame children
|
||||
if (recursive && drawable->derived_type() == PyObjectsEnum::UIFRAME) {
|
||||
auto frame = std::static_pointer_cast<UIFrame>(drawable);
|
||||
// Create temporary collection object for recursive call
|
||||
PyTypeObject* collType = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "UICollection");
|
||||
if (collType) {
|
||||
PyUICollectionObject* child_coll = (PyUICollectionObject*)collType->tp_alloc(collType, 0);
|
||||
if (child_coll) {
|
||||
child_coll->data = frame->children;
|
||||
PyObject* child_results = find(child_coll, args, kwds);
|
||||
if (child_results && PyList_Check(child_results)) {
|
||||
// Extend results with child results
|
||||
for (Py_ssize_t i = 0; i < PyList_Size(child_results); i++) {
|
||||
PyObject* item = PyList_GetItem(child_results, i);
|
||||
Py_INCREF(item);
|
||||
PyList_Append(results, item);
|
||||
Py_DECREF(item);
|
||||
}
|
||||
Py_DECREF(child_results);
|
||||
}
|
||||
Py_DECREF(child_coll);
|
||||
}
|
||||
Py_DECREF(collType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
} else {
|
||||
// Return first exact match or None
|
||||
for (auto& drawable : *vec) {
|
||||
if (drawable->name == pattern) {
|
||||
return convertDrawableToPython(drawable);
|
||||
}
|
||||
|
||||
// Recursive search into Frame children
|
||||
if (recursive && drawable->derived_type() == PyObjectsEnum::UIFRAME) {
|
||||
auto frame = std::static_pointer_cast<UIFrame>(drawable);
|
||||
PyTypeObject* collType = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "UICollection");
|
||||
if (collType) {
|
||||
PyUICollectionObject* child_coll = (PyUICollectionObject*)collType->tp_alloc(collType, 0);
|
||||
if (child_coll) {
|
||||
child_coll->data = frame->children;
|
||||
PyObject* result = find(child_coll, args, kwds);
|
||||
Py_DECREF(child_coll);
|
||||
Py_DECREF(collType);
|
||||
if (result && result != Py_None) {
|
||||
return result;
|
||||
}
|
||||
Py_XDECREF(result);
|
||||
} else {
|
||||
Py_DECREF(collType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
PyMethodDef UICollection::methods[] = {
|
||||
{"append", (PyCFunction)UICollection::append, METH_O},
|
||||
{"extend", (PyCFunction)UICollection::extend, METH_O},
|
||||
{"remove", (PyCFunction)UICollection::remove, METH_O},
|
||||
{"index", (PyCFunction)UICollection::index_method, METH_O},
|
||||
{"count", (PyCFunction)UICollection::count, METH_O},
|
||||
{"append", (PyCFunction)UICollection::append, METH_O,
|
||||
"Add an element to the end of the collection"},
|
||||
{"extend", (PyCFunction)UICollection::extend, METH_O,
|
||||
"Add all elements from an iterable to the collection"},
|
||||
{"remove", (PyCFunction)UICollection::remove, METH_O,
|
||||
"Remove element at the given index"},
|
||||
{"index", (PyCFunction)UICollection::index_method, METH_O,
|
||||
"Return the index of an element in the collection"},
|
||||
{"count", (PyCFunction)UICollection::count, METH_O,
|
||||
"Count occurrences of an element in the collection"},
|
||||
{"find", (PyCFunction)UICollection::find, METH_VARARGS | METH_KEYWORDS,
|
||||
"find(name, recursive=False) -> element or list\n\n"
|
||||
"Find elements by name.\n\n"
|
||||
"Args:\n"
|
||||
" name (str): Name to search for. Supports wildcards:\n"
|
||||
" - 'exact' for exact match (returns single element or None)\n"
|
||||
" - 'prefix*' for starts-with match (returns list)\n"
|
||||
" - '*suffix' for ends-with match (returns list)\n"
|
||||
" - '*substring*' for contains match (returns list)\n"
|
||||
" recursive (bool): If True, search in Frame children recursively.\n\n"
|
||||
"Returns:\n"
|
||||
" Single element if exact match, list if wildcard, None if not found."},
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ public:
|
|||
static PyObject* remove(PyUICollectionObject* self, PyObject* o);
|
||||
static PyObject* index_method(PyUICollectionObject* self, PyObject* value);
|
||||
static PyObject* count(PyUICollectionObject* self, PyObject* value);
|
||||
static PyObject* find(PyUICollectionObject* self, PyObject* args, PyObject* kwds);
|
||||
static PyMethodDef methods[];
|
||||
static PyObject* repr(PyUICollectionObject* self);
|
||||
static int init(PyUICollectionObject* self, PyObject* args, PyObject* kwds);
|
||||
|
|
|
|||
125
src/UIGrid.cpp
125
src/UIGrid.cpp
|
|
@ -2216,12 +2216,127 @@ PyMappingMethods UIEntityCollection::mpmethods = {
|
|||
.mp_ass_subscript = (objobjargproc)UIEntityCollection::ass_subscript
|
||||
};
|
||||
|
||||
// Helper function for entity name matching with wildcards
|
||||
static bool matchEntityName(const std::string& name, const std::string& pattern) {
|
||||
if (pattern.find('*') != std::string::npos) {
|
||||
if (pattern == "*") {
|
||||
return true;
|
||||
} else if (pattern.front() == '*' && pattern.back() == '*' && pattern.length() > 2) {
|
||||
std::string substring = pattern.substr(1, pattern.length() - 2);
|
||||
return name.find(substring) != std::string::npos;
|
||||
} else if (pattern.front() == '*') {
|
||||
std::string suffix = pattern.substr(1);
|
||||
return name.length() >= suffix.length() &&
|
||||
name.compare(name.length() - suffix.length(), suffix.length(), suffix) == 0;
|
||||
} else if (pattern.back() == '*') {
|
||||
std::string prefix = pattern.substr(0, pattern.length() - 1);
|
||||
return name.compare(0, prefix.length(), prefix) == 0;
|
||||
}
|
||||
return name == pattern;
|
||||
}
|
||||
return name == pattern;
|
||||
}
|
||||
|
||||
PyObject* UIEntityCollection::find(PyUIEntityCollectionObject* self, PyObject* args, PyObject* kwds) {
|
||||
const char* name = nullptr;
|
||||
|
||||
static const char* kwlist[] = {"name", NULL};
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", const_cast<char**>(kwlist), &name)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
auto list = self->data.get();
|
||||
if (!list) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Collection data is null");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::string pattern(name);
|
||||
bool has_wildcard = (pattern.find('*') != std::string::npos);
|
||||
|
||||
// Get the Entity type for creating Python objects
|
||||
PyTypeObject* entityType = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Entity");
|
||||
if (!entityType) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Could not find Entity type");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (has_wildcard) {
|
||||
// Return list of all matches
|
||||
PyObject* results = PyList_New(0);
|
||||
if (!results) {
|
||||
Py_DECREF(entityType);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (auto& entity : *list) {
|
||||
// Entity name is stored in sprite.name
|
||||
if (matchEntityName(entity->sprite.name, pattern)) {
|
||||
PyUIEntityObject* py_entity = (PyUIEntityObject*)entityType->tp_alloc(entityType, 0);
|
||||
if (!py_entity) {
|
||||
Py_DECREF(results);
|
||||
Py_DECREF(entityType);
|
||||
return NULL;
|
||||
}
|
||||
py_entity->data = entity;
|
||||
py_entity->weakreflist = NULL;
|
||||
|
||||
if (PyList_Append(results, (PyObject*)py_entity) < 0) {
|
||||
Py_DECREF(py_entity);
|
||||
Py_DECREF(results);
|
||||
Py_DECREF(entityType);
|
||||
return NULL;
|
||||
}
|
||||
Py_DECREF(py_entity); // PyList_Append increfs
|
||||
}
|
||||
}
|
||||
|
||||
Py_DECREF(entityType);
|
||||
return results;
|
||||
} else {
|
||||
// Return first exact match or None
|
||||
for (auto& entity : *list) {
|
||||
if (entity->sprite.name == pattern) {
|
||||
PyUIEntityObject* py_entity = (PyUIEntityObject*)entityType->tp_alloc(entityType, 0);
|
||||
if (!py_entity) {
|
||||
Py_DECREF(entityType);
|
||||
return NULL;
|
||||
}
|
||||
py_entity->data = entity;
|
||||
py_entity->weakreflist = NULL;
|
||||
Py_DECREF(entityType);
|
||||
return (PyObject*)py_entity;
|
||||
}
|
||||
}
|
||||
|
||||
Py_DECREF(entityType);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
PyMethodDef UIEntityCollection::methods[] = {
|
||||
{"append", (PyCFunction)UIEntityCollection::append, METH_O},
|
||||
{"extend", (PyCFunction)UIEntityCollection::extend, METH_O},
|
||||
{"remove", (PyCFunction)UIEntityCollection::remove, METH_O},
|
||||
{"index", (PyCFunction)UIEntityCollection::index_method, METH_O},
|
||||
{"count", (PyCFunction)UIEntityCollection::count, METH_O},
|
||||
{"append", (PyCFunction)UIEntityCollection::append, METH_O,
|
||||
"Add an entity to the collection"},
|
||||
{"extend", (PyCFunction)UIEntityCollection::extend, METH_O,
|
||||
"Add all entities from an iterable"},
|
||||
{"remove", (PyCFunction)UIEntityCollection::remove, METH_O,
|
||||
"Remove an entity from the collection"},
|
||||
{"index", (PyCFunction)UIEntityCollection::index_method, METH_O,
|
||||
"Return the index of an entity"},
|
||||
{"count", (PyCFunction)UIEntityCollection::count, METH_O,
|
||||
"Count occurrences of an entity"},
|
||||
{"find", (PyCFunction)UIEntityCollection::find, METH_VARARGS | METH_KEYWORDS,
|
||||
"find(name) -> entity or list\n\n"
|
||||
"Find entities by name.\n\n"
|
||||
"Args:\n"
|
||||
" name (str): Name to search for. Supports wildcards:\n"
|
||||
" - 'exact' for exact match (returns single entity or None)\n"
|
||||
" - 'prefix*' for starts-with match (returns list)\n"
|
||||
" - '*suffix' for ends-with match (returns list)\n"
|
||||
" - '*substring*' for contains match (returns list)\n\n"
|
||||
"Returns:\n"
|
||||
" Single entity if exact match, list if wildcard, None if not found."},
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -143,6 +143,7 @@ public:
|
|||
static PyObject* remove(PyUIEntityCollectionObject* self, PyObject* o);
|
||||
static PyObject* index_method(PyUIEntityCollectionObject* self, PyObject* value);
|
||||
static PyObject* count(PyUIEntityCollectionObject* self, PyObject* value);
|
||||
static PyObject* find(PyUIEntityCollectionObject* self, PyObject* args, PyObject* kwds);
|
||||
static PyMethodDef methods[];
|
||||
static PyObject* repr(PyUIEntityCollectionObject* self);
|
||||
static int init(PyUIEntityCollectionObject* self, PyObject* args, PyObject* kwds);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,209 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Test for UICollection.find() and EntityCollection.find() methods.
|
||||
|
||||
Tests issue #40 (search and replace by name) and #41 (.find on collections).
|
||||
"""
|
||||
|
||||
import mcrfpy
|
||||
import sys
|
||||
|
||||
def test_uicollection_find():
|
||||
"""Test UICollection.find() with exact and wildcard matches."""
|
||||
print("Testing UICollection.find()...")
|
||||
|
||||
# Create a scene with named elements
|
||||
mcrfpy.createScene("test_find")
|
||||
ui = mcrfpy.sceneUI("test_find")
|
||||
|
||||
# Create frames with names
|
||||
frame1 = mcrfpy.Frame(pos=(0, 0), size=(100, 100))
|
||||
frame1.name = "main_frame"
|
||||
ui.append(frame1)
|
||||
|
||||
frame2 = mcrfpy.Frame(pos=(100, 0), size=(100, 100))
|
||||
frame2.name = "sidebar_frame"
|
||||
ui.append(frame2)
|
||||
|
||||
frame3 = mcrfpy.Frame(pos=(200, 0), size=(100, 100))
|
||||
frame3.name = "player_status"
|
||||
ui.append(frame3)
|
||||
|
||||
caption1 = mcrfpy.Caption(text="Hello", pos=(0, 200))
|
||||
caption1.name = "player_name"
|
||||
ui.append(caption1)
|
||||
|
||||
# Create an unnamed element
|
||||
unnamed = mcrfpy.Caption(text="Unnamed", pos=(0, 250))
|
||||
ui.append(unnamed)
|
||||
|
||||
# Test exact match - found
|
||||
result = ui.find("main_frame")
|
||||
assert result is not None, "Exact match should find element"
|
||||
assert result.name == "main_frame", f"Found wrong element: {result.name}"
|
||||
print(" [PASS] Exact match found")
|
||||
|
||||
# Test exact match - not found
|
||||
result = ui.find("nonexistent")
|
||||
assert result is None, "Should return None when not found"
|
||||
print(" [PASS] Not found returns None")
|
||||
|
||||
# Test prefix wildcard (starts with)
|
||||
results = ui.find("player*")
|
||||
assert isinstance(results, list), "Wildcard should return list"
|
||||
assert len(results) == 2, f"Expected 2 matches, got {len(results)}"
|
||||
names = [r.name for r in results]
|
||||
assert "player_status" in names, "player_status should match player*"
|
||||
assert "player_name" in names, "player_name should match player*"
|
||||
print(" [PASS] Prefix wildcard works")
|
||||
|
||||
# Test suffix wildcard (ends with)
|
||||
results = ui.find("*_frame")
|
||||
assert isinstance(results, list), "Wildcard should return list"
|
||||
assert len(results) == 2, f"Expected 2 matches, got {len(results)}"
|
||||
names = [r.name for r in results]
|
||||
assert "main_frame" in names
|
||||
assert "sidebar_frame" in names
|
||||
print(" [PASS] Suffix wildcard works")
|
||||
|
||||
# Test contains wildcard
|
||||
results = ui.find("*bar*")
|
||||
assert isinstance(results, list), "Wildcard should return list"
|
||||
assert len(results) == 1, f"Expected 1 match, got {len(results)}"
|
||||
assert results[0].name == "sidebar_frame"
|
||||
print(" [PASS] Contains wildcard works")
|
||||
|
||||
# Test match all
|
||||
results = ui.find("*")
|
||||
# Should match all named elements (4 named + 1 unnamed with empty name)
|
||||
assert isinstance(results, list), "Match all should return list"
|
||||
assert len(results) == 5, f"Expected 5 matches, got {len(results)}"
|
||||
print(" [PASS] Match all wildcard works")
|
||||
|
||||
# Test empty pattern matches elements with empty names (unnamed elements)
|
||||
result = ui.find("")
|
||||
# The unnamed caption has an empty name, so exact match should find it
|
||||
assert result is not None, "Empty name exact match should find the unnamed element"
|
||||
print(" [PASS] Empty pattern finds unnamed elements")
|
||||
|
||||
print("UICollection.find() tests passed!")
|
||||
return True
|
||||
|
||||
|
||||
def test_entitycollection_find():
|
||||
"""Test EntityCollection.find() with exact and wildcard matches."""
|
||||
print("\nTesting EntityCollection.find()...")
|
||||
|
||||
# Create a grid with entities
|
||||
mcrfpy.createScene("test_entity_find")
|
||||
ui = mcrfpy.sceneUI("test_entity_find")
|
||||
|
||||
grid = mcrfpy.Grid(grid_size=(10, 10), pos=(0, 0), size=(400, 400))
|
||||
ui.append(grid)
|
||||
|
||||
# Add named entities
|
||||
player = mcrfpy.Entity(grid_pos=(1, 1))
|
||||
player.name = "player"
|
||||
grid.entities.append(player)
|
||||
|
||||
enemy1 = mcrfpy.Entity(grid_pos=(2, 2))
|
||||
enemy1.name = "enemy_goblin"
|
||||
grid.entities.append(enemy1)
|
||||
|
||||
enemy2 = mcrfpy.Entity(grid_pos=(3, 3))
|
||||
enemy2.name = "enemy_orc"
|
||||
grid.entities.append(enemy2)
|
||||
|
||||
item = mcrfpy.Entity(grid_pos=(4, 4))
|
||||
item.name = "item_sword"
|
||||
grid.entities.append(item)
|
||||
|
||||
# Test exact match
|
||||
result = grid.entities.find("player")
|
||||
assert result is not None, "Should find player"
|
||||
assert result.name == "player"
|
||||
print(" [PASS] Entity exact match works")
|
||||
|
||||
# Test not found
|
||||
result = grid.entities.find("boss")
|
||||
assert result is None, "Should return None when not found"
|
||||
print(" [PASS] Entity not found returns None")
|
||||
|
||||
# Test prefix wildcard
|
||||
results = grid.entities.find("enemy*")
|
||||
assert isinstance(results, list)
|
||||
assert len(results) == 2, f"Expected 2 enemies, got {len(results)}"
|
||||
print(" [PASS] Entity prefix wildcard works")
|
||||
|
||||
# Test suffix wildcard
|
||||
results = grid.entities.find("*_orc")
|
||||
assert isinstance(results, list)
|
||||
assert len(results) == 1
|
||||
assert results[0].name == "enemy_orc"
|
||||
print(" [PASS] Entity suffix wildcard works")
|
||||
|
||||
print("EntityCollection.find() tests passed!")
|
||||
return True
|
||||
|
||||
|
||||
def test_recursive_find():
|
||||
"""Test recursive find in nested Frame children."""
|
||||
print("\nTesting recursive find in nested frames...")
|
||||
|
||||
mcrfpy.createScene("test_recursive")
|
||||
ui = mcrfpy.sceneUI("test_recursive")
|
||||
|
||||
# Create nested structure
|
||||
parent = mcrfpy.Frame(pos=(0, 0), size=(400, 400))
|
||||
parent.name = "parent"
|
||||
ui.append(parent)
|
||||
|
||||
child = mcrfpy.Frame(pos=(10, 10), size=(200, 200))
|
||||
child.name = "child_frame"
|
||||
parent.children.append(child)
|
||||
|
||||
grandchild = mcrfpy.Caption(text="Deep", pos=(5, 5))
|
||||
grandchild.name = "deep_caption"
|
||||
child.children.append(grandchild)
|
||||
|
||||
# Non-recursive find should not find nested elements
|
||||
result = ui.find("deep_caption")
|
||||
assert result is None, "Non-recursive find should not find nested element"
|
||||
print(" [PASS] Non-recursive doesn't find nested elements")
|
||||
|
||||
# Recursive find should find nested elements
|
||||
result = ui.find("deep_caption", recursive=True)
|
||||
assert result is not None, "Recursive find should find nested element"
|
||||
assert result.name == "deep_caption"
|
||||
print(" [PASS] Recursive find locates nested elements")
|
||||
|
||||
# Recursive wildcard should find all matches
|
||||
results = ui.find("*_frame", recursive=True)
|
||||
assert isinstance(results, list)
|
||||
names = [r.name for r in results]
|
||||
assert "child_frame" in names, "Should find child_frame"
|
||||
print(" [PASS] Recursive wildcard finds nested matches")
|
||||
|
||||
print("Recursive find tests passed!")
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
all_passed = True
|
||||
all_passed &= test_uicollection_find()
|
||||
all_passed &= test_entitycollection_find()
|
||||
all_passed &= test_recursive_find()
|
||||
|
||||
if all_passed:
|
||||
print("\n" + "="*50)
|
||||
print("All find() tests PASSED!")
|
||||
print("="*50)
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("\nSome tests FAILED!")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"\nTest failed with exception: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
Loading…
Reference in New Issue