1+ from unittest .mock import patch
2+
13import requests
24import responses
35from responses .matchers import json_params_matcher
4- from unittest .mock import patch
56
67from roboflow import API_URL
78from roboflow .adapters .rfapi import AnnotationSaveError , ImageUploadError
@@ -13,70 +14,68 @@ class TestProject(RoboflowTest):
1314 def _create_test_dataset (self , images = None ):
1415 """
1516 Create a test dataset with specified images or a default image
16-
17+
1718 Args:
1819 images: List of image dictionaries. If None, a default image will be used.
19-
20+
2021 Returns:
2122 Dictionary representing a parsed dataset
2223 """
2324 if images is None :
24- images = [
25- {
26- "file" : "image1.jpg" ,
27- "split" : "train" ,
28- "annotationfile" : {
29- "file" : "image1.xml"
30- }
31- }
32- ]
33-
34- return {
35- "location" : "/test/location/" ,
36- "images" : images
37- }
38-
39- def _setup_upload_dataset_mocks (self , test_dataset = None , image_return = None , annotation_return = None ,
40- project_created = False , save_annotation_side_effect = None ,
41- upload_image_side_effect = None ):
25+ images = [{"file" : "image1.jpg" , "split" : "train" , "annotationfile" : {"file" : "image1.xml" }}]
26+
27+ return {"location" : "/test/location/" , "images" : images }
28+
29+ def _setup_upload_dataset_mocks (
30+ self ,
31+ test_dataset = None ,
32+ image_return = None ,
33+ annotation_return = None ,
34+ project_created = False ,
35+ save_annotation_side_effect = None ,
36+ upload_image_side_effect = None ,
37+ ):
4238 """
4339 Set up common mocks for upload_dataset tests
44-
40+
4541 Args:
4642 test_dataset: The dataset to return from parsefolder. If None, creates a default dataset
4743 image_return: Return value for upload_image. Default is successful upload
4844 annotation_return: Return value for save_annotation. Default is successful annotation
4945 project_created: Whether to simulate a newly created project
5046 save_annotation_side_effect: Side effect function for save_annotation
5147 upload_image_side_effect: Side effect function for upload_image
52-
48+
5349 Returns:
5450 Dictionary of mock objects with start and stop methods
5551 """
5652 if test_dataset is None :
5753 test_dataset = self ._create_test_dataset ()
58-
54+
5955 if image_return is None :
6056 image_return = ({"id" : "test-id" , "success" : True }, 0.1 , 0 )
61-
57+
6258 if annotation_return is None :
6359 annotation_return = ({"success" : True }, 0.1 , 0 )
64-
60+
6561 # Create the mock objects
6662 mocks = {
67- 'parser' : patch ('roboflow.core.workspace.folderparser.parsefolder' , return_value = test_dataset ),
68- 'upload' : patch ('roboflow.core.workspace.Project.upload_image' ,
69- side_effect = upload_image_side_effect ) if upload_image_side_effect
70- else patch ('roboflow.core.workspace.Project.upload_image' , return_value = image_return ),
71- 'save_annotation' : patch ('roboflow.core.workspace.Project.save_annotation' ,
72- side_effect = save_annotation_side_effect ) if save_annotation_side_effect
73- else patch ('roboflow.core.workspace.Project.save_annotation' , return_value = annotation_return ),
74- 'get_project' : patch ('roboflow.core.workspace.Workspace._get_or_create_project' ,
75- return_value = (self .project , project_created ))
63+ "parser" : patch ("roboflow.core.workspace.folderparser.parsefolder" , return_value = test_dataset ),
64+ "upload" : patch ("roboflow.core.workspace.Project.upload_image" , side_effect = upload_image_side_effect )
65+ if upload_image_side_effect
66+ else patch ("roboflow.core.workspace.Project.upload_image" , return_value = image_return ),
67+ "save_annotation" : patch (
68+ "roboflow.core.workspace.Project.save_annotation" , side_effect = save_annotation_side_effect
69+ )
70+ if save_annotation_side_effect
71+ else patch ("roboflow.core.workspace.Project.save_annotation" , return_value = annotation_return ),
72+ "get_project" : patch (
73+ "roboflow.core.workspace.Workspace._get_or_create_project" , return_value = (self .project , project_created )
74+ ),
7675 }
77-
76+
7877 return mocks
79-
78+
8079 def test_check_valid_image_with_accepted_formats (self ):
8180 images_to_test = [
8281 "rabbit.JPG" ,
@@ -292,35 +291,27 @@ def test_create_annotation_job_error(self):
292291 )
293292
294293 self .assertEqual (str (context .exception ), "Batch not found" )
295-
294+
296295 @ordered
297296 @responses .activate
298297 def test_project_upload_dataset (self ):
299298 """Test upload_dataset functionality with various scenarios"""
300299 test_scenarios = [
301300 {
302301 "name" : "string_annotationdesc" ,
303- "dataset" : [{
304- "file" : "test_image.jpg" ,
305- "split" : "train" ,
306- "annotationfile" : "string_annotation.txt"
307- }],
302+ "dataset" : [{"file" : "test_image.jpg" , "split" : "train" , "annotationfile" : "string_annotation.txt" }],
308303 "params" : {"num_workers" : 1 },
309- "assertions" : {}
304+ "assertions" : {},
310305 },
311306 {
312307 "name" : "success_basic" ,
313308 "dataset" : [
314309 {"file" : "image1.jpg" , "split" : "train" , "annotationfile" : {"file" : "image1.xml" }},
315- {"file" : "image2.jpg" , "split" : "valid" , "annotationfile" : {"file" : "image2.xml" }}
310+ {"file" : "image2.jpg" , "split" : "valid" , "annotationfile" : {"file" : "image2.xml" }},
316311 ],
317312 "params" : {},
318- "assertions" : {
319- "parser" : [("/test/dataset" ,)],
320- "upload" : {"count" : 2 },
321- "save_annotation" : {"count" : 2 }
322- },
323- "image_return" : ({"id" : "test-id-1" , "success" : True }, 0.1 , 0 )
313+ "assertions" : {"parser" : [("/test/dataset" ,)], "upload" : {"count" : 2 }, "save_annotation" : {"count" : 2 }},
314+ "image_return" : ({"id" : "test-id-1" , "success" : True }, 0.1 , 0 ),
324315 },
325316 {
326317 "name" : "custom_parameters" ,
@@ -330,120 +321,108 @@ def test_project_upload_dataset(self):
330321 "project_license" : "CC BY 4.0" ,
331322 "project_type" : "classification" ,
332323 "batch_name" : "test-batch" ,
333- "num_retries" : 3
324+ "num_retries" : 3 ,
334325 },
335- "assertions" : {
336- "upload" : {"count" : 1 , "kwargs" : {"batch_name" : "test-batch" , "num_retry_uploads" : 3 }}
337- }
326+ "assertions" : {"upload" : {"count" : 1 , "kwargs" : {"batch_name" : "test-batch" , "num_retry_uploads" : 3 }}},
338327 },
339328 {
340329 "name" : "project_creation" ,
341330 "dataset" : None ,
342331 "params" : {"project_name" : "new-project" },
343332 "assertions" : {},
344- "project_created" : True
333+ "project_created" : True ,
345334 },
346335 {
347336 "name" : "with_labelmap" ,
348- "dataset" : [{
349- "file" : "image1.jpg" ,
350- "split" : "train" ,
351- "annotationfile" : {
352- "file" : "image1.xml" ,
353- "labelmap" : "path/to/labelmap.json"
337+ "dataset" : [
338+ {
339+ "file" : "image1.jpg" ,
340+ "split" : "train" ,
341+ "annotationfile" : {"file" : "image1.xml" , "labelmap" : "path/to/labelmap.json" },
354342 }
355- } ],
343+ ],
356344 "params" : {},
357- "assertions" : {
358- "save_annotation" : {"count" : 1 },
359- "load_labelmap" : {"count" : 1 }
360- },
345+ "assertions" : {"save_annotation" : {"count" : 1 }, "load_labelmap" : {"count" : 1 }},
361346 "extra_mocks" : [
362- ("load_labelmap" , "roboflow.core.workspace.load_labelmap" , {"return_value" : {"old_label" : "new_label" }})
363- ]
347+ (
348+ "load_labelmap" ,
349+ "roboflow.core.workspace.load_labelmap" ,
350+ {"return_value" : {"old_label" : "new_label" }},
351+ )
352+ ],
364353 },
365354 {
366355 "name" : "concurrent_uploads" ,
367356 "dataset" : [{"file" : f"image{ i } .jpg" , "split" : "train" } for i in range (10 )],
368357 "params" : {"num_workers" : 5 },
369- "assertions" : {
370- "thread_pool" : {"count" : 1 , "kwargs" : {"max_workers" : 5 }}
371- },
372- "extra_mocks" : [
373- ("thread_pool" , "concurrent.futures.ThreadPoolExecutor" , {})
374- ]
375- },
376- {
377- "name" : "empty_dataset" ,
378- "dataset" : [],
379- "params" : {},
380- "assertions" : {
381- "upload" : {"count" : 0 }
382- }
358+ "assertions" : {"thread_pool" : {"count" : 1 , "kwargs" : {"max_workers" : 5 }}},
359+ "extra_mocks" : [("thread_pool" , "concurrent.futures.ThreadPoolExecutor" , {})],
383360 },
361+ {"name" : "empty_dataset" , "dataset" : [], "params" : {}, "assertions" : {"upload" : {"count" : 0 }}},
384362 {
385363 "name" : "raw_text_annotation" ,
386- "dataset" : [{
387- "file" : "image1.jpg" ,
388- "split" : "train" ,
389- "annotationfile" : {
390- "rawText" : "annotation content here" ,
391- "format" : "json"
364+ "dataset" : [
365+ {
366+ "file" : "image1.jpg" ,
367+ "split" : "train" ,
368+ "annotationfile" : {"rawText" : "annotation content here" , "format" : "json" },
392369 }
393- } ],
370+ ],
394371 "params" : {},
395- "assertions" : {
396- "save_annotation" : {"count" : 1 }
397- }
398- }
372+ "assertions" : {"save_annotation" : {"count" : 1 }},
373+ },
399374 ]
400-
375+
401376 error_cases = [
402377 {
403378 "name" : "image_upload_error" ,
404379 "side_effect" : {
405- "upload_image_side_effect" : lambda * args , ** kwargs :
406- (_ for _ in ()).throw (ImageUploadError ("Failed to upload image" ))
380+ "upload_image_side_effect" : lambda * args , ** kwargs : (_ for _ in ()).throw (
381+ ImageUploadError ("Failed to upload image" )
382+ )
407383 },
408- "params" : {"num_workers" : 1 }
384+ "params" : {"num_workers" : 1 },
409385 },
410386 {
411387 "name" : "annotation_upload_error" ,
412388 "side_effect" : {
413- "save_annotation_side_effect" : lambda * args , ** kwargs :
414- (_ for _ in ()).throw (AnnotationSaveError ("Failed to save annotation" ))
389+ "save_annotation_side_effect" : lambda * args , ** kwargs : (_ for _ in ()).throw (
390+ AnnotationSaveError ("Failed to save annotation" )
391+ )
415392 },
416- "params" : {"num_workers" : 1 }
417- }
393+ "params" : {"num_workers" : 1 },
394+ },
418395 ]
419-
396+
420397 for scenario in test_scenarios :
421- test_dataset = self ._create_test_dataset (scenario .get ("dataset" )) if scenario .get ("dataset" ) is not None else None
422-
398+ test_dataset = (
399+ self ._create_test_dataset (scenario .get ("dataset" )) if scenario .get ("dataset" ) is not None else None
400+ )
401+
423402 extra_mocks = {}
424403 if "extra_mocks" in scenario :
425404 for mock_name , target , config in scenario .get ("extra_mocks" , []):
426405 extra_mocks [mock_name ] = patch (target , ** config )
427-
406+
428407 mocks = self ._setup_upload_dataset_mocks (
429408 test_dataset = test_dataset ,
430409 image_return = scenario .get ("image_return" ),
431- project_created = scenario .get ("project_created" , False )
410+ project_created = scenario .get ("project_created" , False ),
432411 )
433-
412+
434413 mock_objects = {}
435414 for name , mock in mocks .items ():
436415 mock_objects [name ] = mock .start ()
437-
416+
438417 for name , mock in extra_mocks .items ():
439418 mock_objects [name ] = mock .start ()
440-
419+
441420 try :
442421 params = {"dataset_path" : "/test/dataset" , "project_name" : PROJECT_NAME }
443422 params .update (scenario .get ("params" , {}))
444-
423+
445424 self .workspace .upload_dataset (** params )
446-
425+
447426 for mock_name , assertion in scenario .get ("assertions" , {}).items ():
448427 if isinstance (assertion , list ):
449428 mock_obj = mock_objects .get (mock_name )
@@ -461,13 +440,13 @@ def test_project_upload_dataset(self):
461440 finally :
462441 for mock in list (mocks .values ()) + list (extra_mocks .values ()):
463442 mock .stop ()
464-
443+
465444 for case in error_cases :
466445 mocks = self ._setup_upload_dataset_mocks (** case .get ("side_effect" , {}))
467-
446+
468447 for mock in mocks .values ():
469448 mock .start ()
470-
449+
471450 try :
472451 params = {"dataset_path" : "/test/dataset" , "project_name" : PROJECT_NAME }
473452 params .update (case .get ("params" , {}))
0 commit comments