[ba1d1e9] | 1 | """ |
---|
| 2 | Random test-case generator for Real-space simulation |
---|
| 3 | |
---|
| 4 | @copyright: University of Tennessee, 2007 |
---|
| 5 | @license: This software is provided as part of the DANSE project. |
---|
| 6 | """ |
---|
| 7 | import time |
---|
| 8 | |
---|
| 9 | class Stimulus: |
---|
| 10 | """Base class for simuli objects""" |
---|
| 11 | |
---|
| 12 | frequency = 1.0 |
---|
| 13 | |
---|
| 14 | def __init__(self): |
---|
| 15 | """Initialization""" |
---|
| 16 | name = self.__class__.__name__ |
---|
| 17 | pos = name.index('Stimulus') |
---|
| 18 | self.name = name[0:pos] |
---|
| 19 | |
---|
| 20 | def __call__(self, obj): |
---|
| 21 | """ |
---|
| 22 | Stimulus application |
---|
| 23 | @param obj: object to apply the stimulus to |
---|
| 24 | @return: the modified object and the StimulusReport object |
---|
| 25 | """ |
---|
| 26 | return obj, None |
---|
| 27 | |
---|
| 28 | |
---|
| 29 | class StimulusReport: |
---|
| 30 | """Report on the application of a stimulus""" |
---|
| 31 | def __init__(self, tag=''): |
---|
| 32 | """ |
---|
| 33 | Initialization |
---|
| 34 | @param tag: name of the applied stimulus |
---|
| 35 | """ |
---|
| 36 | self.tag = tag |
---|
| 37 | self.applied = 1 |
---|
| 38 | self.passed = 0 |
---|
| 39 | self.trace = '' |
---|
| 40 | self.log = '' |
---|
| 41 | |
---|
| 42 | def __str__(self): |
---|
| 43 | """Returns a string with the success rate""" |
---|
| 44 | return str(self.passed/self.applied) |
---|
| 45 | |
---|
| 46 | def __add__(self, other): |
---|
| 47 | """Adds two reports""" |
---|
| 48 | self.applied += other.applied |
---|
| 49 | self.passed += other.passed |
---|
| 50 | if len(other.trace)>0: |
---|
| 51 | self.trace = self.trace+'\n'+other.trace |
---|
| 52 | if len(other.log)>0: |
---|
| 53 | self.log = self.log+'\n'+other.log |
---|
| 54 | return self |
---|
| 55 | |
---|
| 56 | def isPassed(self): |
---|
| 57 | """Returns true if report is a success""" |
---|
| 58 | if self.applied == self.passed: |
---|
| 59 | return True |
---|
| 60 | return False |
---|
| 61 | |
---|
| 62 | |
---|
| 63 | class ReportCard: # pylint: disable-msg=R0902 |
---|
| 64 | """ Class to hold test-case results """ |
---|
| 65 | |
---|
| 66 | def __init__(self): |
---|
| 67 | """ Initialization """ |
---|
| 68 | ## Number of test cases |
---|
| 69 | self.n_cases = 0 |
---|
| 70 | ## Number of passed test cases |
---|
| 71 | self.n_cases_pass = 0 |
---|
| 72 | |
---|
| 73 | ## Dictionary of stimuli |
---|
| 74 | self.stimuli = {} |
---|
| 75 | |
---|
| 76 | ## Test log |
---|
| 77 | self.log = "" |
---|
| 78 | ## Trace |
---|
| 79 | self.trace = "" |
---|
| 80 | ## Model tested |
---|
| 81 | self.model = "" |
---|
| 82 | ## List of all models tested |
---|
| 83 | self.modelList = [] |
---|
| 84 | |
---|
| 85 | |
---|
| 86 | def __add__(self, other): |
---|
| 87 | """ Add two report cards |
---|
| 88 | @param other: other report to add |
---|
| 89 | @return: updated self |
---|
| 90 | """ |
---|
| 91 | if other.__class__.__name__=='ReportCard': |
---|
| 92 | # Adding two test-case report cards |
---|
| 93 | self.n_cases += other.n_cases |
---|
| 94 | self.n_cases_pass += other.n_cases_pass |
---|
| 95 | |
---|
| 96 | for tag in other.stimuli: |
---|
| 97 | if tag in self.stimuli: |
---|
| 98 | self.stimuli[tag] += other.stimuli[tag] |
---|
| 99 | else: |
---|
| 100 | self.stimuli[tag] = other.stimuli[tag] |
---|
| 101 | |
---|
| 102 | if len(other.log)>0: |
---|
| 103 | self.log += other.log |
---|
| 104 | if len(other.trace)>0: |
---|
| 105 | self.trace += other.trace |
---|
| 106 | |
---|
| 107 | if not other.model in self.modelList: |
---|
| 108 | self.modelList.append(other.model) |
---|
| 109 | |
---|
| 110 | elif other.__class__.__name__ == 'StimulusReport': |
---|
| 111 | # Adding a stimulus report to the report card |
---|
| 112 | if other.tag in self.stimuli: |
---|
| 113 | self.stimuli[other.tag] += other |
---|
| 114 | else: |
---|
| 115 | self.stimuli[other.tag] = other |
---|
| 116 | |
---|
| 117 | else: |
---|
| 118 | raise ValueError, \ |
---|
| 119 | "ReportCard: Unrecognized class %s" % other.__class__.__name__ |
---|
| 120 | return self |
---|
| 121 | |
---|
| 122 | def isPassed(self): |
---|
| 123 | """ Returns true if no error was found """ |
---|
| 124 | if self.n_cases_pass < self.n_cases: |
---|
| 125 | return False |
---|
| 126 | return True |
---|
| 127 | |
---|
| 128 | def __str__(self): |
---|
| 129 | """ String representation of the report card """ |
---|
| 130 | from sans.models.ModelFactory import ModelFactory |
---|
| 131 | |
---|
| 132 | log = '' |
---|
| 133 | |
---|
| 134 | rep = "Detailed output:\n" |
---|
| 135 | rep += self.log |
---|
| 136 | rep += "\n" |
---|
| 137 | rep += "Total number of cases: %g\n" % self.n_cases |
---|
| 138 | rep += " Passed: %g\n" % self.n_cases_pass |
---|
| 139 | rep += "\n" |
---|
| 140 | self.modelList.sort() |
---|
| 141 | rep += "Models tested: %s\n" % self.modelList |
---|
| 142 | rep += "\n" |
---|
| 143 | rep += "\n" |
---|
| 144 | rep += "Breakdown:\n" |
---|
| 145 | |
---|
| 146 | for tag in self.stimuli: |
---|
| 147 | |
---|
| 148 | rep += " %-10s: %g / %g\n" % (tag, self.stimuli[tag].passed, self.stimuli[tag].applied) |
---|
| 149 | log += self.stimuli[tag].log |
---|
| 150 | |
---|
| 151 | rep += '\n'+log |
---|
| 152 | return rep |
---|
| 153 | |
---|
| 154 | |
---|
| 155 | |
---|
| 156 | class TestCaseGenerator: |
---|
| 157 | """ Generator for suite of test-cases |
---|
| 158 | """ |
---|
| 159 | |
---|
| 160 | def __init__(self, stimuli): |
---|
| 161 | """ Initialization |
---|
| 162 | """ |
---|
| 163 | self.stimuli = stimuli |
---|
| 164 | self.n_tests = 0 |
---|
| 165 | self.n_passed = 0 |
---|
| 166 | self.time = 0 |
---|
| 167 | self.reportCard = ReportCard() |
---|
| 168 | |
---|
| 169 | def generateFiles(self, number, file_prefix): |
---|
| 170 | """ Generate test-case files |
---|
| 171 | @param number: number of files to generate |
---|
| 172 | @param file_prefix: prefix for the file names |
---|
| 173 | """ |
---|
| 174 | |
---|
| 175 | for i in range(number): |
---|
| 176 | filename = "%s_%d.xml" % (file_prefix, i) |
---|
| 177 | self.generateFileTest(filename) |
---|
| 178 | self.n_tests += 1 |
---|
| 179 | |
---|
| 180 | def generateAndRun(self, number): |
---|
| 181 | """ Generate test-cases and run them |
---|
| 182 | @param number: number of test-cases to generate |
---|
| 183 | """ |
---|
| 184 | start_time = time.time() |
---|
| 185 | for i in range(number): |
---|
| 186 | textcase = self.generateTest() |
---|
| 187 | t = TestCase(self.stimuli, text = textcase) |
---|
| 188 | passed = t.run() |
---|
| 189 | self.reportCard += t.reportCard |
---|
| 190 | if not passed: |
---|
| 191 | t = time.localtime() |
---|
| 192 | xmloutput = open("error_%i-%i-%i-%i-%i-%i_%i.xml" % \ |
---|
| 193 | (t[0],t[1],t[2],t[3],t[4],t[5],self.reportCard.n_cases),'w') |
---|
| 194 | xmloutput.write(textcase) |
---|
| 195 | xmloutput.close() |
---|
| 196 | |
---|
| 197 | |
---|
| 198 | self.time += time.time()-start_time |
---|
| 199 | print self.reportCard |
---|
| 200 | |
---|
| 201 | def generateFileTest(self, filename = "tmp.xml"): |
---|
| 202 | """ |
---|
| 203 | Write a random test-case in an XML file |
---|
| 204 | @param filename: name of file to write to |
---|
| 205 | """ |
---|
| 206 | text = self.generateTest() |
---|
| 207 | # Write test case in file |
---|
| 208 | xmlfile = open(filename,'w') |
---|
| 209 | xmlfile.write(text) |
---|
| 210 | xmlfile.close() |
---|
| 211 | |
---|
| 212 | |
---|
| 213 | def generateTest(self): |
---|
| 214 | """ |
---|
| 215 | Generate an XML representation of a random test-case |
---|
| 216 | """ |
---|
| 217 | import random |
---|
| 218 | |
---|
| 219 | # Reset stimuli object |
---|
| 220 | self.stimuli.reset() |
---|
| 221 | #t = TestCase() |
---|
| 222 | text = "<?xml version=\"1.0\"?>\n" |
---|
| 223 | text += "<TestCase>\n" |
---|
| 224 | loop = True |
---|
| 225 | while loop: |
---|
| 226 | |
---|
| 227 | stimulus = self.stimuli.getRandomStimulus() |
---|
| 228 | if stimulus == None: |
---|
| 229 | loop = False |
---|
| 230 | else: |
---|
| 231 | text += " <Stimulus id=\"%s\"/>\n" % stimulus |
---|
| 232 | text += "</TestCase>" |
---|
| 233 | |
---|
| 234 | return text |
---|
| 235 | |
---|
| 236 | |
---|
| 237 | class TestCase: |
---|
| 238 | """ Test-case class """ |
---|
| 239 | |
---|
| 240 | def __init__(self, stimuliObject, filename = None, text = None): |
---|
| 241 | """ Initialization |
---|
| 242 | @param filename: name of file containing the test case |
---|
| 243 | """ |
---|
| 244 | self.filename = filename |
---|
| 245 | self.text = text |
---|
| 246 | self.stimuli = stimuliObject |
---|
| 247 | self.model = stimuliObject.setup() |
---|
| 248 | self.passed = True |
---|
| 249 | self.reportCard = ReportCard() |
---|
| 250 | |
---|
| 251 | |
---|
| 252 | def run(self): |
---|
| 253 | """ Read the test case and execute it """ |
---|
| 254 | from xml.dom.minidom import parse |
---|
| 255 | from xml.dom.minidom import parseString |
---|
| 256 | |
---|
| 257 | # Initialize report |
---|
| 258 | self.reportCard = ReportCard() |
---|
| 259 | self.reportCard.model = "Canvas" |
---|
| 260 | self.reportCard.n_cases = 1 |
---|
| 261 | |
---|
| 262 | if not self.text == None: |
---|
| 263 | dom = parseString(self.text) |
---|
| 264 | elif not self.filename == None: |
---|
| 265 | dom = parse(self.filename) |
---|
| 266 | else: |
---|
| 267 | print "No input to parse" |
---|
| 268 | return False |
---|
| 269 | |
---|
| 270 | if dom.hasChildNodes(): |
---|
| 271 | for n in dom.childNodes: |
---|
| 272 | if n.nodeName == "TestCase": |
---|
| 273 | self.processStimuli(n) |
---|
| 274 | |
---|
| 275 | # Update report card |
---|
| 276 | if self.passed: |
---|
| 277 | self.reportCard.n_cases_pass = 1 |
---|
| 278 | |
---|
| 279 | return self.passed |
---|
| 280 | |
---|
| 281 | def processStimuli(self, node): |
---|
| 282 | """ Process the stimuli list in the TestCase node |
---|
| 283 | of an XML test-case file |
---|
| 284 | @param node: test-case node |
---|
| 285 | """ |
---|
| 286 | report = ReportCard() |
---|
| 287 | report.trace = "%s\n" % self.model.name |
---|
| 288 | |
---|
| 289 | self.passed = True |
---|
| 290 | if node.hasChildNodes(): |
---|
| 291 | for n in node.childNodes: |
---|
| 292 | if n.nodeName == "Stimulus": |
---|
| 293 | |
---|
| 294 | s_type = None |
---|
| 295 | if n.hasAttributes(): |
---|
| 296 | # Get stilumus ID |
---|
| 297 | for i in range(len(n.attributes)): |
---|
| 298 | attr_name = n.attributes.item(i).name |
---|
| 299 | if attr_name == "id": |
---|
| 300 | s_type = n.attributes.item(i).nodeValue |
---|
| 301 | if hasattr(self.stimuli,"%sStimulus" % s_type): |
---|
| 302 | stimulus = getattr(self.stimuli,"%sStimulus" % s_type)() |
---|
| 303 | #print s_type, self.model.name |
---|
| 304 | m, res = stimulus(self.model) |
---|
| 305 | #print " ", m.name |
---|
| 306 | self.model = m |
---|
| 307 | if not res.isPassed(): |
---|
| 308 | self.passed = False |
---|
| 309 | |
---|
| 310 | report += res |
---|
| 311 | |
---|
| 312 | else: |
---|
| 313 | print "Stimulus %s not found" % s_type |
---|
| 314 | |
---|
| 315 | self.reportCard += report |
---|
| 316 | |
---|
| 317 | if not self.passed: |
---|
| 318 | print report.trace |
---|
| 319 | return self.passed |
---|
| 320 | |
---|
| 321 | |
---|