#!/usr/bin/env python import unittest import sys import math import StringIO import copy import re import testbase import gradient import fract4dc import fractal import translate import fractparser from gradient import Blend, ColorMode wood='''GIMP Gradient Name: Wood 2 9 0.000000 0.069491 0.138982 1.000000 0.700000 0.400000 1.000000 0.944844 0.616991 0.289137 1.000000 3 0 0.138982 0.208472 0.277963 0.800000 0.522406 0.244813 1.000000 0.928860 0.592934 0.257008 1.000000 3 0 0.277963 0.347454 0.416945 0.820000 0.523444 0.226888 1.000000 0.922120 0.582791 0.243462 1.000000 3 0 0.416945 0.486436 0.555927 0.770000 0.486649 0.203299 1.000000 0.920000 0.579600 0.239200 1.000000 3 0 0.555927 0.609140 0.662354 0.780000 0.491400 0.202800 1.000000 0.903086 0.568944 0.234802 1.000000 4 0 0.662354 0.715568 0.768781 0.810000 0.510300 0.210600 1.000000 0.850329 0.535708 0.221086 1.000000 4 0 0.768781 0.821995 0.875209 0.760000 0.478800 0.197600 1.000000 0.708598 0.446417 0.184235 1.000000 4 0 0.875209 0.928422 0.981636 0.620000 0.390600 0.161200 1.000000 0.000000 0.000000 0.000000 1.000000 4 0 0.981636 0.991653 1.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 0.000000 4 0 ''' class Test(testbase.TestBase): def setUp(self): self.white = [1.0, 1.0, 1.0, 1.0] self.black = [0.0, 0.0, 0.0, 1.0] self.grey_33 = [1.0/3.0, 1.0/3.0, 1.0/3.0, 1.0] self.mid_grey = [0.5, 0.5, 0.5, 1.0] self.grey_66 = [2.0/3.0, 2.0/3.0, 2.0/3.0, 1.0] self.red = [1.0, 0.0, 0.0, 1.0] self.green = [0.0, 1.0, 0.0, 1.0] self.blue = [0.0, 0.0, 1.0, 1.0] def tearDown(self): pass def testCreate(self): g = gradient.Gradient() self.assertEqual(len(g.segments), 1) self.assertWellFormedGradient(g) def testEquals(self): g1 = gradient.Gradient() g2 = gradient.Gradient() self.failUnless(g1 == g2) # calls __eq__ self.assertEqual(g1,g2) # calls __ne__ g1.segments[0].left_color[0] = 0.7 self.assertNotEqual(g1,g2) def checkColorMapAndGradientEquivalent(self,colorlist,maxdiff=0): grad = gradient.Gradient() grad.load_list(colorlist,maxdiff) self.assertWellFormedGradient(grad) if maxdiff != 0: # don't have any robust metric for how close this will be return cmap = fract4dc.cmap_create(colorlist) for i in xrange(1000): fi = i / 1000.0 (r,g,b,a) = grad.get_color_at(fi) cmap_color = fract4dc.cmap_lookup(cmap, fi) grad_color = (int(r*255.0), int(g*255.0), int(b*255.0), int(a*255.0)) self.assertNearlyEqual( grad_color, cmap_color, "colorlist(%s) = %s but gradient(%s) = %s" % \ (fi, cmap_color, fi, grad_color), 1.5) return grad def checkCGradientAndPyGradientEquivalent(self,grad): # We have 2 sets of gradient-drawing code, in C and Python # check they calculate the same answer self.assertWellFormedGradient(grad) cmap = fract4dc.cmap_create_gradient(grad.segments) for i in xrange(1000): fi = i / 1000.0 (r,g,b,a) = grad.get_color_at(fi) #print "%d: %.17g, %.17g, %.17g, %.17g" % (i,r,g,b,a) cmap_color = fract4dc.cmap_lookup(cmap, fi) grad_color = (int(r*255.0), int(g*255.0), int(b*255.0), int(a*255.0)) self.assertNearlyEqual( grad_color, cmap_color, "colorlist(%s) = %s but gradient(%s) = %s" % \ (fi, cmap_color, fi, grad_color), 1.5) def testFromColormap(self): # check that creating a gradient from a colormap produces the same # output colorlist = [(0.0, 255,255,255, 255), (1.0, 0,0,0,255)] g = gradient.Gradient() g.load_list(colorlist) self.assertEqual(len(g.segments),1) self.assertEqual(g.segments[0].left_color, self.white) self.assertEqual(g.segments[0].right_color, self.black) self.checkColorMapAndGradientEquivalent(colorlist) def testFromUgr(self): pt = fractparser.parser.parse('''blatte10 { gradient: title="blatte10" smooth=no index=0 color=3085069 index=25 color=3216141 index=56 color=10761236 index=83 color=1408165 index=92 color=4050153 index=110 color=18018 index=134 color=0 index=213 color=5183243 index=284 color=11494485 index=358 color=0 index=384 color=144 opacity: smooth=no index=0 opacity=255 } ''') t = translate.GradientFunc(pt.children[0]) g = gradient.Gradient() g.load_ugr(t) self.assertWellFormedGradient(g) self.assertEqual(len(g.segments),12) def testFromColormap2(self): # create a longer one colorlist = [ (0.0, 255,0,0, 255), (0.5, 0,255,0, 255), (1.0, 0, 0, 255, 255)] g = gradient.Gradient() g.load_list(colorlist) self.assertWellFormedGradient(g) self.assertEqual(len(g.segments),2) self.assertEqual(g.segments[0].left_color, self.red) self.assertEqual(g.segments[0].right_color, self.green) self.assertEqual(g.segments[1].left, 0.5) self.assertEqual(g.segments[1].left_color, self.green) self.assertEqual(g.segments[1].right_color, self.blue) self.checkColorMapAndGradientEquivalent(colorlist) def testFromColormap3(self): # create a short, compressible one colorlist = [ (0.0, 0, 0, 0, 255), (0.5, 127, 127, 127, 255), (1.0, 255, 255, 255, 255)] g = gradient.Gradient() g.load_list(colorlist) self.assertWellFormedGradient(g) self.assertEqual(len(g.segments),1) def testfromColormap4(self): # this should always produce 255 segments colorlist = self.colorMapFromFile("../maps/4zebbowx.map") grad = self.checkColorMapAndGradientEquivalent(colorlist) self.assertEqual(len(grad.segments),255) def testfromColormapHlsrain(self): # this map once caused issues colorlist = self.colorMapFromFile("../maps/hlsrain5.map") grad = self.checkColorMapAndGradientEquivalent(colorlist) def testFromColormapCompressible(self): colorlist = self.colorMapFromFile("../maps/Gallet02.map") grad = self.checkColorMapAndGradientEquivalent(colorlist) self.failUnless(len(grad.segments) < 255,"should have been compressed") def testFromColormapLossyCompression(self): colorlist = self.colorMapFromFile("../maps/Gallet02.map") grad = self.checkColorMapAndGradientEquivalent(colorlist, 2) def colorMapFromFile(self, name): f = open(name,"r") i = 0 colorlist = [] rgb_re = re.compile(r'\s*(\d+)\s+(\d+)\s+(\d+)') for line in f: m = rgb_re.match(line) if m != None: (r,g,b) = (int(m.group(1)), int(m.group(2)), int(m.group(3))) # in case they're over 255 - as some badly-behaved # Fractint gradients are [r,g,b] = map(lambda x: min(x,255), [r,g,b]) if i == 0: # first color is inside solid color pass else: colorlist.append(((i-1)/255.0,r,g,b,255)) i += 1 return colorlist def testCopy(self): # check that copy.copy() doesn't share state g1 = self.create_rgb_gradient() s1 = g1.serialize() g2 = copy.copy(g1) s2 =g2.serialize() self.assertEqual(s1,s2) g1.segments[0].bmode = gradient.Blend.CURVED s3=g2.serialize() self.assertEqual(s2,s3) def testGradientCmap(self): g = gradient.Gradient() self.checkCGradientAndPyGradientEquivalent(g) g.segments[0].mid = 0.2 self.checkCGradientAndPyGradientEquivalent(g) g.segments[0].bmode = gradient.Blend.CURVED self.checkCGradientAndPyGradientEquivalent(g) g.segments[0].bmode = gradient.Blend.SINE self.checkCGradientAndPyGradientEquivalent(g) g.segments[0].bmode = gradient.Blend.SPHERE_INCREASING self.checkCGradientAndPyGradientEquivalent(g) g.segments[0].bmode = gradient.Blend.SPHERE_DECREASING self.checkCGradientAndPyGradientEquivalent(g) g = self.create_rgb_gradient() self.checkCGradientAndPyGradientEquivalent(g) def create_rgb_gradient(self): # make a simple gradient which goes from R -> G -> B g = gradient.Gradient() g.segments = [ gradient.Segment(0.0, self.red, 0.333, self.red), gradient.Segment(0.333, self.green, 0.667, self.green), gradient.Segment(0.667, self.blue, 1.0, self.blue)] return g def testGetSegments(self): g = self.create_rgb_gradient() self.assertWellFormedGradient(g) self.assertEqual(g.get_segment_at(0.0), g.segments[0]) self.assertEqual(g.get_segment_at(0.5), g.segments[1]) self.assertEqual(g.get_segment_at(1.0), g.segments[2]) self.assertEqual(g.get_segment_at(0.333), g.segments[0]) self.assertEqual(g.get_segment_at(0.667), g.segments[1]) self.assertRaises(IndexError, g.get_segment_at, -1.0) self.assertRaises(IndexError, g.get_segment_at, 2.0) def testGetSegmentIndexes(self): g = self.create_rgb_gradient() self.assertWellFormedGradient(g) self.assertEqual(g.get_index_at(0.0), 0) self.assertEqual(g.get_index_at(0.5), 1) self.assertEqual(g.get_index_at(1.0), 2) self.assertEqual(g.get_index_at(0.333), 0) self.assertEqual(g.get_index_at(0.667), 1) self.assertRaises(IndexError, g.get_index_at, -1.0) self.assertRaises(IndexError, g.get_index_at, 2.0) def testLinearSegment(self): seg = gradient.Segment(0.0, self.red, 0.333, self.red) self.assertEqual(seg.get_linear_factor(0.0, 0.5),0.0) self.assertEqual(seg.get_linear_factor(0.5, 0.5),0.5) self.assertEqual(seg.get_linear_factor(1.0, 0.5),1.0) # middle close to left end self.assertEqual(seg.get_linear_factor(0.0, 0.0),0.0) self.assertEqual(seg.get_linear_factor(0.5, 0.0),0.75) self.assertEqual(seg.get_linear_factor(1.0, 0.0),1.0) # middle close to right end self.assertEqual(seg.get_linear_factor(0.0, 1.0),0.0) self.assertEqual(seg.get_linear_factor(0.5, 1.0),0.25) self.assertEqual(seg.get_linear_factor(1.0, 1.0),0.5) def testVeryShortSegment(self): seg = gradient.Segment(0.0, self.black, 0.0, self.white) self.assertEqual(seg.get_color_at(0.0), self.mid_grey) self.assertEqual(seg.get_color_at(0.5), self.mid_grey) self.assertEqual(seg.get_color_at(1.0), self.mid_grey) def testGetFlatColors(self): g = self.create_rgb_gradient() self.assertWellFormedGradient(g) self.assertEqual(g.get_color_at(0.0), self.red) self.assertEqual(g.get_color_at(0.3), self.red) self.assertEqual(g.get_color_at(0.333), self.red) self.assertEqual(g.get_color_at(0.334), self.green) self.assertEqual(g.get_color_at(0.666), self.green) self.assertEqual(g.get_color_at(0.668), self.blue) self.assertEqual(g.get_color_at(1.0), self.blue) def checkGreyGradient(self, g, midpoint, oracle): self.assertWellFormedGradient(g) for i in xrange(256): x = i * 1.0/256.0 fx = oracle(x,midpoint) self.failUnless(0.0 <= fx <= 1.0, "dubious value %f for %x" % (fx,x)) col = g.get_color_at(x) expected = [fx,fx,fx,1.0] self.assertNearlyEqual(col, expected, "%s != %s for %s (int %s)" % \ (col, expected, x, i)) # should be 0 at start, 1 at end self.assertEqual(g.get_color_at(0.0), [0.0, 0.0, 0.0, 1.0]) self.assertEqual(g.get_color_at(1.0), [1.0, 1.0, 1.0, 1.0]) # should be halfway at the midpoint self.assertEqual(g.get_color_at(midpoint), [0.5, 0.5, 0.5, 1.0]) def testGreyGradient(self): g = gradient.Gradient() # linear, central midpoint g.segments[0].mid = 0.5 self.checkGreyGradient(g, 0.5, lambda x,mid : x) # linear, off-center midpoint def predict_025_linear(x, dummy): if x <= 0.25: fx = 0.5 * x/0.25 else: fx = 0.5 + 0.5 * (x-0.25)/0.75 return fx g.segments[0].mid = 0.25 self.checkGreyGradient(g, 0.25, predict_025_linear) # curved, central midpoint # (curved = linear if midpoint = 0.5) g.segments[0].bmode = Blend.CURVED g.segments[0].mid = 0.5 self.checkGreyGradient(g, 0.5, lambda x,mid : x) # curved, non-central midpoint, sharper at the start def predict_025_curved(x, dummy): if x == 0.0: fx = 0.0 else: fx = math.pow(x,math.log(0.5)/math.log(0.25)) return fx g.segments[0].mid = 0.25 self.checkGreyGradient(g, 0.25, predict_025_curved) # TODO: sine, sphere_increasing/decreasing def testAlphaChannel(self): g = gradient.Gradient() g.segments[0].left_color[3] = 0.5 self.assertWellFormedGradient(g) for i in xrange(256): x = i * 1.0/256.0 self.assertEqual(g.get_color_at(x), [x,x,x,0.5+x/2.0]) def testSaveSegment(self): s = gradient.Segment( 0.0, [0.0, 0.1, 0.2, 0.3], 0.5, [0.4, 0.5, 0.6, 0.7]) sio = StringIO.StringIO() s.save(sio) self.assertEqual( "0.000000 0.250000 0.500000 0.000000 0.100000 0.200000 0.300000 0.400000 0.500000 0.600000 0.700000 0 0\n", sio.getvalue()) s2 = gradient.Segment( 0.5, [0.4, 0.5, 0.6, 0.7], 0.8, [0.01, 0.02, 0.03, 0.04]) self.assertEqual(True,s.left_of(s2)) self.assertEqual(False,s.left_of(s)) self.assertEqual(False,s2.left_of(s)) self.assertEqual(True,s2.right_of(s)) self.assertEqual(False,s.right_of(s)) self.assertEqual(False,s.right_of(s2)) s2.save(sio) self.assertEqual( """0.000000 0.250000 0.500000 0.000000 0.100000 0.200000 0.300000 0.400000 0.500000 0.600000 0.700000 0 0 0.500000 0.650000 0.800000 0.400000 0.500000 0.600000 0.700000 0.010000 0.020000 0.030000 0.040000 0 0 """, sio.getvalue()) sio = StringIO.StringIO() s.save(sio) s2.save(sio,True) self.assertEqual( """0.000000 0.250000 0.500000 0.000000 0.100000 0.200000 0.300000 0.400000 0.500000 0.600000 0.700000 0 0 +0.650000 0.800000 0.010000 0.020000 0.030000 0.040000 0 0 """, sio.getvalue()) def testSaveMinimal(self): g = gradient.Gradient() s = StringIO.StringIO() g.save(s) self.assertEqual( s.getvalue(), '''GIMP Gradient 1 0.000000 0.500000 1.000000 0.000000 0.000000 0.000000 1.000000 1.000000 1.000000 1.000000 1.000000 0 0 ''') def testLoadSimple(self): g = gradient.Gradient() s = StringIO.StringIO(wood) g.load(s) self.assertEqual(g.name, "Wood 2") self.assertEqual(len(g.segments), 9) # check round trip s2 = StringIO.StringIO() g.save(s2) self.assertEqual(wood,s2.getvalue()) g2 = gradient.Gradient() s2.seek(0,0) g2.load(s2) self.assertEqual(g,g2) def testLoadCompressed(self): g = gradient.Gradient() s = StringIO.StringIO(wood) g.load(s) s2 = StringIO.StringIO() g.save(s2,True) s2.seek(0,0) g_from_comp = gradient.Gradient() g_from_comp.load(s2) self.assertEqual(g,g_from_comp) def checkBadLoad(self, str): g = gradient.Gradient() s = StringIO.StringIO(str) self.assertRaises(Exception, g.load,s) self.assertWellFormedGradient(g) def testBadLoad(self): bad_files=[ '''xxx''', '''GIMP Gradient''', '''GIMP Gradient 17 0.000000 0.500000 1.000000 0.000000 0.000000 0.000000 1.000000 1.000000 1.000000 1.000000 1.000000 0 0 ''', '''GIMP Gradient 1 0.000000 0.500000 1.000000 0.000000 ''', '''GIMP Gradient 1 0.000000 0.500000 1.000000 0.000000 0.000000 0.000000 1.000000 1.000000 1.000000 1.000000 1.000000 foo 0 '''] for file in bad_files: self.checkBadLoad(file) def testSetLeft(self): g = gradient.Gradient() g.add(0) # shouldn't be able to move leftmost segment self.assertEqual(g.set_left(0, -1.0),0.0) self.assertEqual(g.set_left(0,0.2),0.0) # should be able to move other one self.assertEqual(g.set_left(1,0.4),0.4) self.assertEqual(g.set_left(1,0.6),0.6) # but no further than midpoints self.assertEqual(g.set_left(1,0.2),0.25 + gradient.Segment.EPSILON) self.assertEqual(g.set_left(1,0.8),0.75 - gradient.Segment.EPSILON) def testSetRight(self): g = gradient.Gradient() g.add(0) # shouldn't be able to move rightmost segment self.assertEqual(g.set_right(1, -1.0),1.0) self.assertEqual(g.set_right(1,0.2),1.0) # should be able to move other one self.assertEqual(g.set_right(0,0.4),0.4) self.assertEqual(g.set_right(0,0.6),0.6) # but no further than midpoints self.assertEqual(g.set_right(0,0.2),0.25 + gradient.Segment.EPSILON) self.assertEqual(g.set_right(0,0.8),0.75 - gradient.Segment.EPSILON) def testSetMiddle(self): g = gradient.Gradient() # should be able to move self.assertEqual(g.set_middle(0,0.4),0.4) self.assertEqual(g.set_middle(0,0.6),0.6) # but no further than endpoints self.assertEqual(g.set_middle(0,0.0),0.0 + gradient.Segment.EPSILON) self.assertEqual(g.set_middle(0,1.0),1.0 - gradient.Segment.EPSILON) def testAddSegment(self): # add some segments, ensure invariants hold and correct # outcome occurs. g = gradient.Gradient() # add a segment at left-hand end g.add(0) self.assertWellFormedGradient(g) self.assertEqual(len(g.segments),2) left = g.segments[0] right = g.segments[1] self.assertEqual(left.right_color, right.left_color) self.assertEqual(left.right_color,[0.5,0.5,0.5,1.0]) self.assertEqual(right.right_color, [1.0, 1.0, 1.0, 1.0]) self.assertEqual(left.mid, 0.25) self.assertEqual(left.right, 0.5) self.assertEqual(right.mid, 0.75) self.assertEqual(right.right, 1.0) # should have no effect on resulting pattern self.checkGreyGradient(g, 0.5, lambda x,mid : x) # add another one g.add(0) self.assertWellFormedGradient(g) self.assertEqual(len(g.segments),3) left = g.segments[0] right = g.segments[1] self.assertEqual(left.right_color, right.left_color) self.assertEqual(left.right_color,[0.25,0.25,0.25,1.0]) self.assertEqual(left.mid, 0.125) self.assertEqual(left.right, 0.25) self.assertEqual(right.mid, 0.375) self.assertEqual(right.right, 0.5) # still no effect self.checkGreyGradient(g, 0.5, lambda x,mid : x) # one more at the other end g.add(len(g.segments)-1) self.assertWellFormedGradient(g) self.assertEqual(len(g.segments),4) left = g.segments[2] right = g.segments[3] self.assertEqual(left.right_color, right.left_color) self.assertEqual(left.right_color,[0.75,0.75,0.75,1.0]) self.assertEqual(left.mid, 0.625) self.assertEqual(left.right, 0.75) self.assertEqual(right.mid, 0.875) self.assertEqual(right.right, 1.0) # still no effect self.checkGreyGradient(g, 0.5, lambda x,mid : x) def three_segments(self): # set up 3 equal segments return [ gradient.Segment(0,self.black, 1.0/3.0, self.grey_33), gradient.Segment(1.0/3.0,self.grey_33, 2.0/3.0, self.grey_66), gradient.Segment(2.0/3.0,self.grey_66, 1.0, self.white)] def testRemove(self): # test removal of segments g = gradient.Gradient() # shouldn't be able to remove last segment self.assertRaises(gradient.Error, g.remove, 0.0) g.segments = self.three_segments() self.assertWellFormedGradient(g) self.checkGreyGradient(g, 0.5, lambda x,mid : x) # remove middle one g.remove(1) self.assertEqual(len(g.segments),2) self.assertWellFormedGradient(g) self.assertEqual(g.segments[0].right, 0.5) # recreate and remove left one g.segments = self.three_segments() self.assertWellFormedGradient(g) self.checkGreyGradient(g, 0.5, lambda x,mid : x) g.remove(0) self.assertEqual(len(g.segments),2) self.assertWellFormedGradient(g) self.assertEqual(g.segments[0].right, 2.0/3.0) # recreate and remove right one g.segments = self.three_segments() self.assertWellFormedGradient(g) self.checkGreyGradient(g, 0.5, lambda x,mid : x) g.remove(2) self.assertEqual(len(g.segments),2) self.assertWellFormedGradient(g) self.assertEqual(g.segments[1].left, 1.0/3.0) def testRemoveSmooth(self): # test removal of segments g = gradient.Gradient() g.segments = self.three_segments() self.assertWellFormedGradient(g) self.checkGreyGradient(g, 0.5, lambda x,mid : x) # remove middle one g.remove(1, True) self.assertEqual(len(g.segments),2) self.assertWellFormedGradient(g) self.checkGreyGradient(g, 0.5, lambda x,mid : x) self.assertEqual(g.segments[0].right, 0.5) # recreate and remove left one g.segments = self.three_segments() self.assertWellFormedGradient(g) self.checkGreyGradient(g, 0.5, lambda x,mid : x) g.remove(0,True) self.assertEqual(len(g.segments),2) self.assertWellFormedGradient(g) self.checkGreyGradient(g, 0.5, lambda x,mid : x) self.assertEqual(g.segments[0].right, 2.0/3.0) # recreate and remove right one g.segments = self.three_segments() self.assertWellFormedGradient(g) self.checkGreyGradient(g, 0.5, lambda x,mid : x) g.remove(2,True) self.assertEqual(len(g.segments),2) self.assertWellFormedGradient(g) self.checkGreyGradient(g, 0.5, lambda x,mid : x) self.assertEqual(g.segments[1].left, 1.0/3.0) def testLoadCS(self): g = gradient.Gradient() f = open("../testdata/test.cs") g.load_cs(f) self.assertEqual(8,len(g.segments)) self.assertEqual( [ 0x1d/255.0 , 0x10/255.0, 0x10/255.0, 1.0], g.segments[0].left_color) f.seek(0) g.load(f) def assertNearlyEqual(self,a,b,msg, epsilon=1.0e-12): # check that each element is within epsilon of expected value for (ra,rb) in zip(a,b): d = abs(ra-rb) self.failUnless(d < epsilon,msg) def assertWellFormedGradient(self, g): # check starts and sends at 0 and 1 first_seg = g.segments[0] last_seg = g.segments[-1] self.assertEqual(first_seg.left, 0.0) self.assertEqual(last_seg.right, 1.0) # check segments line up and types are in range previous_seg = g.segments[0] for seg in g.segments[1:]: # check offsets self.failUnless(0.0 <= seg.left <= 1.0) self.failUnless(0.0 <= seg.right <= 1.0) self.failUnless( seg.left <= seg.mid <= seg.right, "midpoint %g not between endpoints %g,%g" % \ (seg.mid, seg.left, seg.right)) # check colors self.assertEquals(len(seg.left_color),4) self.assertEquals(len(seg.right_color),4) for x in seg.left_color + seg.right_color: self.failUnless(0.0 <= x <= 1.0) # check modes self.failUnless(Blend.LINEAR <= seg.bmode <= Blend.SPHERE_DECREASING) self.failUnless(ColorMode.RGB <= seg.cmode <= ColorMode.HSV_CW) # check offset chaining self.failUnless(seg.right > seg.left) self.assertEqual(seg.left, previous_seg.right) previous_seg = seg def suite(): return unittest.makeSuite(Test,'test') if __name__ == '__main__': unittest.main(defaultTest='suite')