Skip to content

Commit fd77407

Browse files
[cueman] Add Comprehensive Unit Tests for buildFrameSearch (AcademySoftwareFoundation#1973)
**Link the Issue(s) this Pull Request is related to.** - AcademySoftwareFoundation#1932 **Summarize your change.** This pull request adds a new test module, test_frame_search.py, which provides comprehensive unit tests for the buildFrameSearch function in cueman.main. Test cases included: - Default search construction: Verifies correct output with default arguments. - Layer filter integration: Tests handling of single and multiple layer filters. - Range filter validation: Ensures frame range arguments are processed correctly. - State filter conversion: Mocks state conversion to validate correct state handling. - Memory filter application: Tests memory filter logic, including range and conversion. - Duration filter application: Tests duration filter logic, including range and conversion. - Pagination parameter inclusion: Checks that page number is included in search parameters. - Limit parameter inclusion: Verifies correct handling of result limit. - Empty filter scenarios: Ensures function returns an empty dictionary when no filters are provided. - Edge case handling: Tests None/empty returns from handleIntCriterion. - Combined filters: Validates all filters working simultaneously. - Single value filters: Tests single layer and single state value handling. Additional improvements: - Added Apache 2.0 license header for OpenCue compliance - Added comprehensive docstrings for all classes and methods - Fixed incomplete assertions in memory_filter and duration_filter tests Co-authored-by: Aniket Singh Yadav <singhyadavaniket43@gmail.com> Co-authored-by: Ramon Figueiredo <rfigueiredo@imageworks.com>
1 parent 528023d commit fd77407

File tree

1 file changed

+234
-0
lines changed

1 file changed

+234
-0
lines changed

cueman/tests/test_frame_search.py

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright Contributors to the OpenCue Project
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
18+
"""
19+
Unit tests for buildFrameSearch function in cueman.main
20+
"""
21+
import unittest
22+
from unittest import mock
23+
24+
from cueman import main
25+
26+
try:
27+
from opencue.compiled_proto import job_pb2
28+
except ImportError:
29+
try:
30+
from opencue_proto import job_pb2
31+
except ImportError:
32+
# pylint: disable=too-few-public-methods
33+
class JobPb2Mock:
34+
"""Mock class for job_pb2 when it's not available."""
35+
RUNNING = 'RUNNING'
36+
WAITING = 'WAITING'
37+
job_pb2 = JobPb2Mock()
38+
39+
class TestBuildFrameSearch(unittest.TestCase):
40+
"""Test cases for the buildFrameSearch function in cueman.main."""
41+
def setUp(self):
42+
"""Set up test fixtures with default arguments."""
43+
self.default_args = mock.Mock()
44+
self.default_args.layer = None
45+
self.default_args.range = None
46+
self.default_args.state = None
47+
self.default_args.memory = 0.01
48+
self.default_args.duration = 0.01
49+
self.default_args.page = None
50+
self.default_args.limit = 1000
51+
52+
def test_default_search(self):
53+
"""Test default search construction with default memory and duration."""
54+
with mock.patch("cueadmin.common.handleIntCriterion") as mock_handle:
55+
mock_handle.side_effect = [
56+
[mock.Mock(value=10485)],
57+
[mock.Mock(value=36)],
58+
]
59+
result = main.buildFrameSearch(self.default_args)
60+
expected = {"memory": "0-10485", "duration": "0-36", "limit": 1000}
61+
self.assertEqual(result, expected)
62+
63+
def test_layer_filter(self):
64+
"""Test layer filter integration with single and multiple layers."""
65+
args = mock.Mock(**self.default_args.__dict__)
66+
args.layer = ["layer1", "layer2"]
67+
result = main.buildFrameSearch(args)
68+
self.assertIn("layer", result)
69+
self.assertEqual(result["layer"], ["layer1", "layer2"])
70+
71+
def test_range_filter(self):
72+
"""Test range filter validation for frame ranges."""
73+
args = mock.Mock(**self.default_args.__dict__)
74+
args.range = "1-100"
75+
result = main.buildFrameSearch(args)
76+
self.assertIn("range", result)
77+
self.assertEqual(result["range"], "1-100")
78+
79+
def test_state_filter_conversion(self):
80+
"""Test state filter conversion from string to proper state enum."""
81+
args = mock.Mock(**self.default_args.__dict__)
82+
args.state = ["RUNNING", "WAITING"]
83+
with mock.patch("cueadmin.common.Convert.strToFrameState") as mock_convert:
84+
mock_convert.side_effect = [job_pb2.RUNNING, job_pb2.WAITING]
85+
result = main.buildFrameSearch(args)
86+
self.assertIn("state", result)
87+
self.assertEqual(result["state"], [job_pb2.RUNNING, job_pb2.WAITING])
88+
89+
def test_memory_filter(self):
90+
"""Test memory filter application with range and conversion."""
91+
args = mock.Mock(**self.default_args.__dict__)
92+
args.memory = "2-4"
93+
with mock.patch("cueadmin.common.handleIntCriterion") as mock_handle:
94+
mock_handle.return_value = [
95+
mock.Mock(value=2097152),
96+
mock.Mock(value=4194304),
97+
]
98+
result = main.buildFrameSearch(args)
99+
self.assertIn("memory", result)
100+
self.assertEqual(result["memory"], "4194304")
101+
102+
def test_duration_filter(self):
103+
"""Test duration filter application with range and conversion."""
104+
args = mock.Mock(**self.default_args.__dict__)
105+
args.duration = "1-2"
106+
with mock.patch("cueadmin.common.handleIntCriterion") as mock_handle:
107+
mock_handle.return_value = [
108+
mock.Mock(value=3600),
109+
mock.Mock(value=7200),
110+
]
111+
result = main.buildFrameSearch(args)
112+
self.assertIn("duration", result)
113+
self.assertEqual(result["duration"], "7200")
114+
115+
def test_pagination_inclusion(self):
116+
"""Test that page number is included in search parameters."""
117+
args = mock.Mock(**self.default_args.__dict__)
118+
args.page = 2
119+
result = main.buildFrameSearch(args)
120+
self.assertIn("page", result)
121+
self.assertEqual(result["page"], 2)
122+
123+
def test_limit_inclusion(self):
124+
"""Test that result limit is correctly handled."""
125+
args = mock.Mock(**self.default_args.__dict__)
126+
args.limit = 500
127+
result = main.buildFrameSearch(args)
128+
self.assertIn("limit", result)
129+
self.assertEqual(result["limit"], 500)
130+
131+
def test_empty_filters(self):
132+
"""Test that function returns empty dict when no filters are provided."""
133+
args = mock.Mock()
134+
args.layer = None
135+
args.range = None
136+
args.state = None
137+
args.memory = None
138+
args.duration = None
139+
args.page = None
140+
args.limit = None
141+
result = main.buildFrameSearch(args)
142+
self.assertEqual(result, {})
143+
144+
def test_single_layer_filter(self):
145+
"""Test layer filter with a single layer."""
146+
args = mock.Mock(**self.default_args.__dict__)
147+
args.layer = ["single_layer"]
148+
result = main.buildFrameSearch(args)
149+
self.assertIn("layer", result)
150+
self.assertEqual(result["layer"], ["single_layer"])
151+
152+
def test_memory_filter_no_results(self):
153+
"""Test memory filter when handleIntCriterion returns None."""
154+
args = mock.Mock(**self.default_args.__dict__)
155+
args.memory = "invalid"
156+
with mock.patch("cueadmin.common.handleIntCriterion") as mock_handle:
157+
mock_handle.return_value = None
158+
result = main.buildFrameSearch(args)
159+
self.assertNotIn("memory", result)
160+
161+
def test_duration_filter_no_results(self):
162+
"""Test duration filter when handleIntCriterion returns None."""
163+
args = mock.Mock(**self.default_args.__dict__)
164+
args.duration = "invalid"
165+
with mock.patch("cueadmin.common.handleIntCriterion") as mock_handle:
166+
mock_handle.return_value = None
167+
result = main.buildFrameSearch(args)
168+
self.assertNotIn("duration", result)
169+
170+
def test_combined_filters(self):
171+
"""Test multiple filters applied simultaneously."""
172+
args = mock.Mock()
173+
args.layer = ["layer1"]
174+
args.range = "1-50"
175+
args.state = ["RUNNING"]
176+
args.memory = 2.0
177+
args.duration = 1.0
178+
args.page = 1
179+
args.limit = 100
180+
181+
with mock.patch("cueadmin.common.Convert.strToFrameState") as mock_convert, \
182+
mock.patch("cueadmin.common.handleIntCriterion") as mock_handle:
183+
mock_convert.return_value = job_pb2.RUNNING
184+
mock_handle.side_effect = [
185+
[mock.Mock(value=2097152)], # memory
186+
[mock.Mock(value=3600)], # duration
187+
]
188+
result = main.buildFrameSearch(args)
189+
190+
self.assertIn("layer", result)
191+
self.assertIn("range", result)
192+
self.assertIn("state", result)
193+
self.assertIn("memory", result)
194+
self.assertIn("duration", result)
195+
self.assertIn("page", result)
196+
self.assertIn("limit", result)
197+
self.assertEqual(result["layer"], ["layer1"])
198+
self.assertEqual(result["range"], "1-50")
199+
self.assertEqual(result["state"], [job_pb2.RUNNING])
200+
self.assertEqual(result["memory"], "2097152")
201+
self.assertEqual(result["duration"], "3600")
202+
self.assertEqual(result["page"], 1)
203+
self.assertEqual(result["limit"], 100)
204+
205+
def test_state_single_value(self):
206+
"""Test state filter with a single state value."""
207+
args = mock.Mock(**self.default_args.__dict__)
208+
args.state = ["RUNNING"]
209+
with mock.patch("cueadmin.common.Convert.strToFrameState") as mock_convert:
210+
mock_convert.return_value = job_pb2.RUNNING
211+
result = main.buildFrameSearch(args)
212+
self.assertIn("state", result)
213+
self.assertEqual(result["state"], [job_pb2.RUNNING])
214+
215+
def test_memory_filter_empty_list(self):
216+
"""Test memory filter when handleIntCriterion returns an empty list."""
217+
args = mock.Mock(**self.default_args.__dict__)
218+
args.memory = 0
219+
with mock.patch("cueadmin.common.handleIntCriterion") as mock_handle:
220+
mock_handle.return_value = []
221+
result = main.buildFrameSearch(args)
222+
self.assertNotIn("memory", result)
223+
224+
def test_duration_filter_empty_list(self):
225+
"""Test duration filter when handleIntCriterion returns an empty list."""
226+
args = mock.Mock(**self.default_args.__dict__)
227+
args.duration = 0
228+
with mock.patch("cueadmin.common.handleIntCriterion") as mock_handle:
229+
mock_handle.return_value = []
230+
result = main.buildFrameSearch(args)
231+
self.assertNotIn("duration", result)
232+
233+
if __name__ == "__main__":
234+
unittest.main()

0 commit comments

Comments
 (0)