@@ -444,7 +444,7 @@ def test_op_different__attribute_value(self, op_leniency):
444444 lmetadata = self .cls (** self .lvalues )
445445 rmetadata = self .cls (** self .rvalues )
446446
447- # Result contains entirely EMPTY attributes (either strict or lenient).
447+ # Result has entirely EMPTY attributes (whether strict or lenient).
448448 # TODO: is this maybe a mistake of the existing implementation ?
449449 expected = self .lvalues .copy ()
450450 expected ["attributes" ] = None
@@ -457,30 +457,31 @@ def test_op_different__attribute_value(self, op_leniency):
457457 assert rmetadata .combine (lmetadata )._asdict () == expected
458458
459459
460- class Test_difference (tests .IrisTest ):
461- def setUp (self ):
462- self .values = dict (
460+ class Test_difference :
461+ @pytest .fixture (autouse = True )
462+ def setup (self ):
463+ self .lvalues = dict (
463464 standard_name = sentinel .standard_name ,
464465 long_name = sentinel .long_name ,
465466 var_name = sentinel .var_name ,
466467 units = sentinel .units ,
467- attributes = sentinel . attributes ,
468+ attributes = dict (), # MUST be a dict
468469 cell_methods = sentinel .cell_methods ,
469470 )
471+ # Make a copy with all-different objects in it.
472+ self .rvalues = deepcopy (self .lvalues )
470473 self .dummy = sentinel .dummy
471474 self .cls = CubeMetadata
472475 self .none = self .cls (* (None ,) * len (self .cls ._fields ))
473476
474477 def test_wraps_docstring (self ):
475- self .assertEqual (
476- BaseMetadata .difference .__doc__ , self .cls .difference .__doc__
477- )
478+ assert self .cls .difference .__doc__ == BaseMetadata .difference .__doc__
478479
479480 def test_lenient_service (self ):
480481 qualname_difference = _qualname (self .cls .difference )
481- self . assertIn ( qualname_difference , _LENIENT )
482- self . assertTrue ( _LENIENT [qualname_difference ])
483- self . assertTrue ( _LENIENT [self .cls .difference ])
482+ assert qualname_difference in _LENIENT
483+ assert _LENIENT [qualname_difference ]
484+ assert _LENIENT [self .cls .difference ]
484485
485486 def test_lenient_default (self ):
486487 other = sentinel .other
@@ -490,11 +491,8 @@ def test_lenient_default(self):
490491 ) as mocker :
491492 result = self .none .difference (other )
492493
493- self .assertEqual (return_value , result )
494- self .assertEqual (1 , mocker .call_count )
495- (arg ,), kwargs = mocker .call_args
496- self .assertEqual (other , arg )
497- self .assertEqual (dict (lenient = None ), kwargs )
494+ assert return_value == result
495+ assert mocker .call_args_list == [mock .call (other , lenient = None )]
498496
499497 def test_lenient (self ):
500498 other = sentinel .other
@@ -505,178 +503,124 @@ def test_lenient(self):
505503 ) as mocker :
506504 result = self .none .difference (other , lenient = lenient )
507505
508- self .assertEqual (return_value , result )
509- self .assertEqual (1 , mocker .call_count )
510- (arg ,), kwargs = mocker .call_args
511- self .assertEqual (other , arg )
512- self .assertEqual (dict (lenient = lenient ), kwargs )
506+ assert return_value == result
507+ assert mocker .call_args_list == [mock .call (other , lenient = lenient )]
513508
514- def test_op_lenient_same (self ):
515- lmetadata = self .cls (** self .values )
516- rmetadata = self .cls (** self .values )
517-
518- with mock .patch ("iris.common.metadata._LENIENT" , return_value = True ):
519- self .assertIsNone (lmetadata .difference (rmetadata ))
520- self .assertIsNone (rmetadata .difference (lmetadata ))
521-
522- def test_op_lenient_same_none (self ):
523- lmetadata = self .cls (** self .values )
524- right = self .values .copy ()
525- right ["var_name" ] = None
526- rmetadata = self .cls (** right )
527-
528- with mock .patch ("iris.common.metadata._LENIENT" , return_value = True ):
529- self .assertIsNone (lmetadata .difference (rmetadata ))
530- self .assertIsNone (rmetadata .difference (lmetadata ))
531-
532- def test_op_lenient_same_cell_methods_none (self ):
533- lmetadata = self .cls (** self .values )
534- right = self .values .copy ()
535- right ["cell_methods" ] = None
536- rmetadata = self .cls (** right )
537- lexpected = deepcopy (self .none )._asdict ()
538- lexpected ["cell_methods" ] = (sentinel .cell_methods , None )
539- rexpected = deepcopy (self .none )._asdict ()
540- rexpected ["cell_methods" ] = (None , sentinel .cell_methods )
541-
542- with mock .patch ("iris.common.metadata._LENIENT" , return_value = True ):
543- self .assertEqual (
544- lexpected , lmetadata .difference (rmetadata )._asdict ()
545- )
546- self .assertEqual (
547- rexpected , rmetadata .difference (lmetadata )._asdict ()
548- )
509+ def test_op_same (self , op_leniency ):
510+ is_lenient = op_leniency == "lenient"
511+ lmetadata = self .cls (** self .lvalues )
512+ rmetadata = self .cls (** self .rvalues )
549513
550- def test_op_lenient_different (self ):
551- left = self .values .copy ()
552- lmetadata = self .cls (** left )
553- right = self .values .copy ()
554- right ["units" ] = self .dummy
555- rmetadata = self .cls (** right )
556- lexpected = deepcopy (self .none )._asdict ()
557- lexpected ["units" ] = (left ["units" ], right ["units" ])
558- rexpected = deepcopy (self .none )._asdict ()
559- rexpected ["units" ] = lexpected ["units" ][::- 1 ]
560-
561- with mock .patch ("iris.common.metadata._LENIENT" , return_value = True ):
562- self .assertEqual (
563- lexpected , lmetadata .difference (rmetadata )._asdict ()
564- )
565- self .assertEqual (
566- rexpected , rmetadata .difference (lmetadata )._asdict ()
567- )
514+ with mock .patch (
515+ "iris.common.metadata._LENIENT" , return_value = is_lenient
516+ ):
517+ assert lmetadata .difference (rmetadata ) is None
518+ assert rmetadata .difference (lmetadata ) is None
568519
569- def test_op_lenient_different_cell_methods (self ):
570- left = self .values .copy ()
571- lmetadata = self .cls (** left )
572- right = self .values .copy ()
573- right ["cell_methods" ] = self .dummy
574- rmetadata = self .cls (** right )
575- lexpected = deepcopy (self .none )._asdict ()
576- lexpected ["cell_methods" ] = (
577- left ["cell_methods" ],
578- right ["cell_methods" ],
579- )
580- rexpected = deepcopy (self .none )._asdict ()
581- rexpected ["cell_methods" ] = lexpected ["cell_methods" ][::- 1 ]
520+ def test_op_different__none (self , fieldname , op_leniency ):
521+ if fieldname in ("attributes" ,): # 'units'):
522+ # These cannot properly be set to 'None'. Tested elsewhere.
523+ pytest .skip ()
582524
583- with mock .patch ("iris.common.metadata._LENIENT" , return_value = True ):
584- self .assertEqual (
585- lexpected , lmetadata .difference (rmetadata )._asdict ()
586- )
587- self .assertEqual (
588- rexpected , rmetadata .difference (lmetadata )._asdict ()
589- )
525+ is_lenient = op_leniency == "lenient"
526+
527+ lmetadata = self .cls (** self .lvalues )
528+ self .rvalues [fieldname ] = None
529+ rmetadata = self .cls (** self .rvalues )
590530
591- def test_op_strict_same (self ):
592- lmetadata = self .cls (** self .values )
593- rmetadata = self .cls (** self .values )
594-
595- with mock .patch ("iris.common.metadata._LENIENT" , return_value = False ):
596- self .assertIsNone (lmetadata .difference (rmetadata ))
597- self .assertIsNone (rmetadata .difference (lmetadata ))
598-
599- def test_op_strict_different (self ):
600- left = self .values .copy ()
601- lmetadata = self .cls (** left )
602- right = self .values .copy ()
603- right ["long_name" ] = self .dummy
604- rmetadata = self .cls (** right )
605- lexpected = deepcopy (self .none )._asdict ()
606- lexpected ["long_name" ] = (left ["long_name" ], right ["long_name" ])
607- rexpected = deepcopy (self .none )._asdict ()
608- rexpected ["long_name" ] = lexpected ["long_name" ][::- 1 ]
609-
610- with mock .patch ("iris.common.metadata._LENIENT" , return_value = False ):
611- self .assertEqual (
612- lexpected , lmetadata .difference (rmetadata )._asdict ()
531+ if fieldname in ("units" , "cell_methods" ):
532+ # These ones are always "strict"
533+ strict_result = True
534+ elif fieldname in ("standard_name" , "long_name" , "var_name" ):
535+ strict_result = not is_lenient
536+ else :
537+ # Ensure we are handling all the different field cases
538+ raise ValueError (
539+ f"{ self .__name__ } unhandled fieldname : { fieldname } "
613540 )
614- self .assertEqual (
615- rexpected , rmetadata .difference (lmetadata )._asdict ()
541+
542+ if strict_result :
543+ diffentry = tuple (
544+ [getattr (mm , fieldname ) for mm in (lmetadata , rmetadata )]
616545 )
546+ lexpected = self .none ._asdict ()
547+ lexpected [fieldname ] = diffentry
548+ rexpected = lexpected .copy ()
549+ rexpected [fieldname ] = diffentry [::- 1 ]
550+ # NOTE: in these cases, the difference metadata will fail an == operation,
551+ # because of the 'None' entries.
552+ # But we can use metadata._asdict() and test that.
617553
618- def test_op_strict_different_cell_methods (self ):
619- left = self .values .copy ()
620- lmetadata = self .cls (** left )
621- right = self .values .copy ()
622- right ["cell_methods" ] = self .dummy
623- rmetadata = self .cls (** right )
624- lexpected = deepcopy (self .none )._asdict ()
625- lexpected ["cell_methods" ] = (
626- left ["cell_methods" ],
627- right ["cell_methods" ],
628- )
629- rexpected = deepcopy (self .none )._asdict ()
630- rexpected ["cell_methods" ] = lexpected ["cell_methods" ][::- 1 ]
554+ with mock .patch (
555+ "iris.common.metadata._LENIENT" , return_value = is_lenient
556+ ):
557+ if strict_result :
558+ assert lmetadata .difference (rmetadata )._asdict () == lexpected
559+ assert rmetadata .difference (lmetadata )._asdict () == rexpected
560+ else :
561+ # Expect NO differences
562+ assert lmetadata .difference (rmetadata ) is None
563+ assert rmetadata .difference (lmetadata ) is None
631564
632- with mock .patch ("iris.common.metadata._LENIENT" , return_value = False ):
633- self .assertEqual (
634- lexpected , lmetadata .difference (rmetadata )._asdict ()
635- )
636- self .assertEqual (
637- rexpected , rmetadata .difference (lmetadata )._asdict ()
638- )
565+ def test_op_different__attribute_extra (self , op_leniency ):
566+ is_lenient = op_leniency == "lenient"
567+ self .lvalues ["attributes" ] = {"_a_common_" : self .dummy }
568+ lmetadata = self .cls (** self .lvalues )
569+ rvalues = deepcopy (self .lvalues )
570+ rvalues ["attributes" ]["_b_extra_" ] = mock .sentinel .extra
571+ rmetadata = self .cls (** rvalues )
572+
573+ if not is_lenient :
574+ # In this case, attributes returns a "difference dictionary"
575+ diffentry = tuple ([{}, {"_b_extra_" : mock .sentinel .extra }])
576+ lexpected = self .none ._asdict ()
577+ lexpected ["attributes" ] = diffentry
578+ rexpected = lexpected .copy ()
579+ rexpected ["attributes" ] = diffentry [::- 1 ]
639580
640- def test_op_strict_different_none (self ):
641- left = self .values .copy ()
642- lmetadata = self .cls (** left )
643- right = self .values .copy ()
644- right ["long_name" ] = None
645- rmetadata = self .cls (** right )
646- lexpected = deepcopy (self .none )._asdict ()
647- lexpected ["long_name" ] = (left ["long_name" ], right ["long_name" ])
648- rexpected = deepcopy (self .none )._asdict ()
649- rexpected ["long_name" ] = lexpected ["long_name" ][::- 1 ]
650-
651- with mock .patch ("iris.common.metadata._LENIENT" , return_value = False ):
652- self .assertEqual (
653- lexpected , lmetadata .difference (rmetadata )._asdict ()
654- )
655- self .assertEqual (
656- rexpected , rmetadata .difference (lmetadata )._asdict ()
657- )
581+ with mock .patch (
582+ "iris.common.metadata._LENIENT" , return_value = is_lenient
583+ ):
584+ if is_lenient :
585+ # It recognises no difference
586+ assert lmetadata .difference (rmetadata ) is None
587+ assert rmetadata .difference (lmetadata ) is None
588+ else :
589+ # As calculated above
590+ assert lmetadata .difference (rmetadata )._asdict () == lexpected
591+ assert rmetadata .difference (lmetadata )._asdict () == rexpected
592+
593+ def test_op_different__attribute_value (self , op_leniency ):
594+ is_lenient = op_leniency == "lenient"
595+ self .lvalues ["attributes" ] = {
596+ "_a_common_" : self .dummy ,
597+ "_b_extra_" : mock .sentinel .value1 ,
598+ }
599+ lmetadata = self .cls (** self .lvalues )
600+ self .rvalues ["attributes" ] = {
601+ "_a_common_" : self .dummy ,
602+ "_b_extra_" : mock .sentinel .value2 ,
603+ }
604+ rmetadata = self .cls (** self .rvalues )
658605
659- def test_op_strict_different_measure_none (self ):
660- left = self .values .copy ()
661- lmetadata = self .cls (** left )
662- right = self .values .copy ()
663- right ["cell_methods" ] = None
664- rmetadata = self .cls (** right )
665- lexpected = deepcopy (self .none )._asdict ()
666- lexpected ["cell_methods" ] = (
667- left ["cell_methods" ],
668- right ["cell_methods" ],
606+ # In this case, attributes returns a "difference dictionary"
607+ diffentry = tuple (
608+ [
609+ {"_b_extra_" : mock .sentinel .value1 },
610+ {"_b_extra_" : mock .sentinel .value2 },
611+ ]
669612 )
670- rexpected = deepcopy (self .none )._asdict ()
671- rexpected ["cell_methods" ] = lexpected ["cell_methods" ][::- 1 ]
613+ lexpected = self .none ._asdict ()
614+ lexpected ["attributes" ] = diffentry
615+ rexpected = lexpected .copy ()
616+ rexpected ["attributes" ] = diffentry [::- 1 ]
672617
673- with mock .patch ("iris.common.metadata._LENIENT" , return_value = False ):
674- self .assertEqual (
675- lexpected , lmetadata .difference (rmetadata )._asdict ()
676- )
677- self .assertEqual (
678- rexpected , rmetadata .difference (lmetadata )._asdict ()
679- )
618+ with mock .patch (
619+ "iris.common.metadata._LENIENT" , return_value = is_lenient
620+ ):
621+ # As calculated above -- same for both strict + lenient
622+ assert lmetadata .difference (rmetadata )._asdict () == lexpected
623+ assert rmetadata .difference (lmetadata )._asdict () == rexpected
680624
681625
682626class Test_equal (tests .IrisTest ):
0 commit comments