README.testutils 56 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202
  1. =========================================
  2. 0. Outline
  3. =========================================
  4. To aid in the development of MCell tests, I've developed a number of
  5. utilities. Most of these utilities are specific test types that may be
  6. applied to specific MCell runs to analyze various aspects of the run. Some
  7. are infrastructure for the test suite itself. A few are general utility
  8. functions or classes. I'll start with the general utilities, then I'll
  9. discuss the infrastructure, and finally the specific test types. All of these
  10. are defined in Python modules in the system_tests subdirectory, and are set up
  11. so that they may be imported into any test suite.
  12. Most of the specific test types exist in two forms. One will have a name
  13. similar to:
  14. assertFileExists
  15. and the other will have a name like:
  16. RequireFileExists
  17. The former is a simple function call which performs the test. The latter is
  18. an object version which encapsulates the test. (The reason for this will
  19. become clear when I discuss the infrastructure, but essentially, the setup for
  20. a test involves adding 0 or more encapsulated tests using the
  21. 'add_extra_check' method on an McellTest object. It will automatically run
  22. all of the tests at the end after the run completes.) The object version does
  23. not run the test as soon as it is constructed; instead, it stores the details
  24. of the call and calls the corresponding assert* version when its own 'check'
  25. method is called. That is:
  26. r = RequireFileExists(filename)
  27. r.check()
  28. is equivalent to:
  29. assertFileExists(filename)
  30. Most tests will include a quick summary of the arguments to the test. Within
  31. the argument lists, I'm using the shorthand of:
  32. function_name(argument1,
  33. argument2,
  34. argument3='Argentina',
  35. argument4=1764,
  36. argument5=None)
  37. to indicate that argument1 and argument2 are required, and that argument3,
  38. argument4, and argument5 are optional and may be provided via the normal Python
  39. mechanisms (either as keyword arguments, or as extra positional arguments), and
  40. that if they are not provided,their respective defaults will be 'Argentina',
  41. 1764, and None.
  42. At the very end will be a discussion of some test-specific utilities I've
  43. developed which can be found in the individual test suite directories. (For
  44. instance, there is a base class which handles all of the commonalities in the
  45. "parser" tests, and another for the macromolecule tests.)
  46. Most of the tests will be fairly formulaic, and you may be able to get by
  47. largely by copying existing tests and modifying them appropriately, but this
  48. document should serve as a reasonable reference to the utilities to help write
  49. more specialized tests. (There may even be tests which do not fit will into
  50. this framework, which will require some sleight-of-hand. See test_009 in
  51. mdl/testsuite/regression/test_regression.py for one such example. In the
  52. future, we should probably add slightly better support for testing situations
  53. which require multiple runs punctuated by checkpoint/restore cycles.)
  54. 1. General utilities
  55. 2. Infrastructure
  56. 3. Specific test types
  57. 4. Test-specific utilities
  58. =========================================
  59. 1. General utilities
  60. =========================================
  61. testutils module:
  62. All of these utilities may be imported from testutils.
  63. ----
  64. safe_concat: concatenate two lists or tuples, treating None as
  65. an empty list
  66. safe_concat([1], [2]) => [1, 2]
  67. safe_concat((1,), (2,)) => (1, 2)
  68. safe_concat([1], None) => [1]
  69. safe_concat(None, (2,)) => (2,)
  70. safe_concat(None, None) => None
  71. ----
  72. get_output_dir: get the top-level output directory for the testsuite
  73. get_output_dir() => "/path/to/test_results"
  74. ----
  75. crange: like 'range', but closed on both ends (range is open
  76. on the top)
  77. crange(1, 10) => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  78. crange(5, 1, -1) => [5, 4, 3, 2, 1]
  79. crange(0, 5, 2) => [0, 2, 4]
  80. crange(9, 0, -2) => [9, 7, 5, 3, 1]
  81. ----
  82. cleandir: nuke an entire directory subtree
  83. cleandir("/etc") => PANIC! PLEASE DO NOT DO THIS!
  84. ----
  85. assertFileExists: throw an error if the specified filename doesn't
  86. exist.
  87. RequireFileExists: object form of the same test
  88. assertFileExists("/etc/passwd") => nothing happens (unless you
  89. ran the cleandir command
  90. before...)
  91. assertFileExists("/xxxxxxxxxx") => exception thrown
  92. RequireFileExists("/etc/passwd").check() => nothing happens
  93. RequireFileExists("/xxxxxxxxxx").check() => exception thrown
  94. ----
  95. assertFileNotExists: throw an error if the specified filename does
  96. exist.
  97. RequireFileNotExists: object form of the same test
  98. assertFileNotExists("/etc/passwd") => exception thrown (unless
  99. you ran the cleandir
  100. command before...)
  101. assertFileNotExists("/xxxxxxxxxx") => nothing happens
  102. RequireFileNotExists("/etc/passwd").check() => exception thrown
  103. RequireFileNotExists("/xxxxxxxxxx").check() => nothing happens
  104. ----
  105. assertFileEmpty: throw an error if the specified filename does not refer
  106. to an existing, but empty file
  107. RequireFileEmpty: object form of the same test
  108. # These tests assume that /tmp/emptyfile exists and is 0 bytes long
  109. assertFileEmpty("/etc/passwd") => exception thrown (hopefully)
  110. assertFileEmpty("/tmp/emptyfile") => nothing happens
  111. assertFileEmpty("/tmp/xxxxxxxxx") => nothing happens
  112. RequireFileEmpty("/etc/passwd").check() => exception thrown
  113. RequireFileEmpty("/tmp/emptyfile").check() => nothing happens
  114. RequireFileEmpty("/tmp/xxxxxxxxx").check() => nothing happens
  115. ----
  116. assertFileNonempty: throw an error if the specified filename does not
  117. refer to an existing, non-empty file [optionally
  118. checking that the file size exactly matches an
  119. expected size]
  120. RequireFileNonempty: object form of the same test
  121. # These tests assume that /tmp/emptyfile exists and is 0 bytes long
  122. # These tests assume that /tmp/file_304 exists and is 304 bytes long
  123. assertFileNonempty("/etc/passwd") => nothing happens (hopefully)
  124. assertFileNonempty("/tmp/emptyfile") => exception thrown
  125. assertFileNonempty("/tmp/xxxxxxxxx") => exception thrown
  126. assertFileNonempty("/tmp/file_304") => nothing happens
  127. assertFileNonempty("/tmp/file_304", 304) => nothing happens
  128. assertFileNonempty("/tmp/file_304", 305) => exception thrown
  129. RequireFileNonempty("/etc/passwd").check() => nothing happens
  130. RequireFileNonempty("/tmp/emptyfile").check() => exception thrown
  131. RequireFileNonempty("/tmp/xxxxxxxxx").check() => exception thrown
  132. RequireFileNonempty("/tmp/file_304").check() => nothing happens
  133. RequireFileNonempty("/tmp/file_304", 304).check() => nothing happens
  134. RequireFileNonempty("/tmp/file_304", 305).check() => exception thrown
  135. ----
  136. assertFileEquals: throw an error if the specified filename does not
  137. refer to an existing file, or if the contents of the
  138. file do not exactly match a provided string
  139. RequireFileEquals: object form of the same test
  140. # These tests assume that /tmp/emptyfile exists and is 0 bytes long
  141. # These tests assume that /tmp/hw exists and has contents "hello world"
  142. assertFileEquals("/etc/passwd", "hello world") => exception
  143. assertFileEquals("/tmp/emptyfile", "hello world") => exception
  144. assertFileEquals("/tmp/xxxxxxxxx", "hello world") => exception
  145. assertFileEquals("/tmp/hw", "hello world") => nothing happens
  146. RequireFileEquals("/etc/passwd", "hello world").check() => exception
  147. RequireFileEquals("/tmp/emptyfile", "hello world").check() => exception
  148. RequireFileEquals("/tmp/xxxxxxxxx", "hello world").check() => exception
  149. RequireFileEquals("/tmp/hw", "hello world").check() => nothing happens
  150. ----
  151. assertFileMatches: throw an error if the specified filename does not
  152. refer to an existing file, or if the contents of the
  153. file do not match a provided regular expression. By
  154. default, the file is required to match the regular
  155. expression at least once, but potentially an
  156. unlimited number of times. This may be changed by
  157. providing the 'expectMinMatches' and/or
  158. expectMaxMatches keyword arguments.
  159. RequireFileMatches: object form of the same test
  160. # These tests assume that /tmp/emptyfile exists and is 0 bytes long
  161. # These tests assume that /tmp/hw exists and has contents "hello world"
  162. assertFileMatches("/tmp/emptyfile", "h[elowr ]*d") => exception
  163. assertFileMatches("/tmp/xxxxxxxxx", "h[elowr ]*d") => exception
  164. assertFileMatches("/tmp/hw", "h[elowr ]*d") => nothing happens
  165. assertFileMatches("/tmp/hw", "h[elowr ]*d", 2) => exception
  166. assertFileMatches("/tmp/hw", "l", 2, 3) => nothing happens
  167. assertFileMatches("/tmp/hw", "l", 2, 2) => exception
  168. assertFileMatches("/tmp/hw", "l", 4, 6) => exception
  169. RequireFileMatches("/etc/passwd", "h[elowr ]*d").check() => exception
  170. RequireFileMatches("/tmp/emptyfile", "h[elowr ]*d").check() => exception
  171. RequireFileMatches("/tmp/xxxxxxxxx", "h[elowr ]*d").check() => exception
  172. RequireFileMatches("/tmp/hw", "h[elowr ]*d").check() => nothing happens
  173. RequireFileMatches("/tmp/hw", "h[elowr ]*d", 2).check() => exception
  174. RequireFileMatches("/tmp/hw", "l", 2, 3).check() => nothing happens
  175. RequireFileMatches("/tmp/hw", "l", 2, 2).check() => exception
  176. RequireFileMatches("/tmp/hw", "l", 4, 6).check() => exception
  177. ----
  178. assertFileSymlink: throw an error if the specified filename does not
  179. refer to a symlink, and optionally, if the symlink
  180. does not point to a specific location.
  181. RequireFileSymlink: object form of the same test
  182. # These tests assume that /bin/sh is a symlink to "bash", as is
  183. # the case on many Linux machines
  184. assertFileSymlink("/bin/sh") => nothing happens
  185. assertFileSymlink("/bin/sh", "bash") => nothing happens
  186. assertFileSymlink("/bin/sh", "tcsh") => wrath of the gods
  187. assertFileSymlink("/etc/passwd") => exception
  188. RequireFileSymlink("/bin/sh").check() => nothing happens
  189. RequireFileSymlink("/bin/sh", "bash").check() => nothing happens
  190. RequireFileSymlink("/bin/sh", "tcsh").check() => wrath of the gods
  191. RequireFileSymlink("/etc/passwd").check() => exception
  192. ----
  193. assertFileDir: throw an error if the specified filename does not refer
  194. to an existing directory.
  195. RequireFileDir: object form of the same test
  196. assertFileDir("/etc") => nothing happens
  197. assertFileDir("/etc/passwd") => exception
  198. assertFileDir("/xxxxxxxxxx") => exception
  199. RequireFileDir("/etc").check() => nothing happens
  200. RequireFileDir("/etc/passwd").check() => exception
  201. RequireFileDir("/xxxxxxxxxx").check() => exception
  202. =========================================
  203. 2. Infrastructure
  204. =========================================
  205. testutils module:
  206. All of these utilities may be imported from testutils.
  207. McellTest class: This class encapsulates the details of an MCell run.
  208. It contains a number of methods to allow specification
  209. of details of how to invoke the run, as well as
  210. specification of the exact criteria for success.
  211. McellTest.rand: random number stream (instance of random.Random)
  212. rand_float = McellTest.rand.uniform(0, 20) # rand float in [0, 20.0)
  213. rand_int = McellTest.rand.randint(0, 200) # rand int in [0, 200)
  214. McellTest.config: configuration object - gets settings from test.cfg
  215. McellTest.config.get("regression", "foo"):
  216. get "foo" setting from "[regression]" section of test.cfg,
  217. or "[DEFAULT]" section if not found in [regression section
  218. McellTest(cat, file, args): create a new McellTest instance. It
  219. will look for its settings preferentially in the section of
  220. test.cfg whose name is passed as cat, but will default to the
  221. '[DEFAULT]' section for any settings not found in the specified
  222. section. mcell will be launched using the mdl file passed as
  223. 'file', and will be given the arguments passed in the args list.
  224. File is a path relative to the script (test_parser.py,
  225. test_regression.py, etc.) that created the McellTest instance.
  226. The test will not run until the 'invoke' method is called.
  227. mt = McellTest('foobar', '01-test_mcell_something.mdl', ['-quiet', '-logfreq', '100])
  228. The above mcell run we've set up will look in the
  229. [foobar] section of test.cfg for the mcell executable to
  230. use, will find 01-test_mcell_something.mdl in the same
  231. directory as the test script which created this
  232. McellTest instance, and it will get (in addition to the
  233. -seed, -logfile, and -errfile arguments) -quiet and
  234. -logfreq 100.
  235. A default constructed mcell test expects to send nothing to
  236. stdin, to receive nothing from stdout/stderr, and expects that
  237. the executable will exit with an exit code of 0.
  238. set_check_std_handles(i, o, e): Sets whether each of stdin, stdout,
  239. stderr should be checked. "checking" stdin simply
  240. means closing stdin immediately so that the program
  241. will get a signal if it tries to read from stdin.
  242. Checking stdout and stderr checks if they are empty
  243. (i.e. produced no output). Most of the runs in the
  244. test suite redirect stdout and stderr to files
  245. 'realout' and 'realerr', so it's usually a good idea to
  246. set these flags. They are set by default, so it is
  247. unnecessary to set them explicitly unless you want
  248. output out stdout/stderr or want to send input to
  249. stdin.
  250. mt = MCellTest(...)
  251. mt.set_check_std_handles(True, False, False) # check stdin, don't check stdout/stderr
  252. mt.set_check_std_handles(1, 0, 0) # check stdin, don't check stdout/stderr
  253. mt.set_check_std_handles(0, 1, 0) # check stdout, don't check stdin/stderr
  254. mt.set_check_std_handles(0, 0, 1) # check stderr, don't check stdin/stdout
  255. set_expected_exit_code(ec): Sets the exit code we expect from mcell
  256. when it exits. This defaults to 0, which means
  257. successful exit. When testing error handling, it may
  258. be appropriate to set the expected exit code to 1. It
  259. is never expected for the process to die due to a
  260. signal.
  261. mt = MCellTest(...)
  262. mt.set_expected_exit_code(1)
  263. add_exist_file(f): Adds to the test the criterion that upon
  264. successful exit of mcell, the file (or files) f must
  265. exist. 'f' must either be a string giving a single
  266. filename, or an iterable (list, tuple, etc.) giving a
  267. collection of filenames.
  268. add_empty_file(f): Adds to the test the criterion that upon
  269. successful exit of mcell, the file (or files) f must
  270. exist and be empty. 'f' must either be a string
  271. giving a single filename, or an iterable (list, tuple,
  272. etc.) giving a collection of filenames.
  273. add_nonempty_file(f, expected_size): Adds to the test the criterion
  274. that upon successful exit of mcell, the file (or
  275. files) f must exist and be nonempty. 'f' must either
  276. be a string giving a single filename, or an iterable
  277. (list, tuple, etc.) giving a collection of filenames.
  278. expected_size is an optional argument, and if
  279. specified, the file (or files) must have size in bytes
  280. exactly matching expected_size, which is an integer.
  281. add_constant_file(f, cnt): Adds to the test the criterion that upon
  282. successful exit of mcell, the file (or files) f must
  283. exist and be nonempty. 'f' must either be a string
  284. giving a single filename, or an iterable (list, tuple,
  285. etc.) giving a collection of filenames. cnt is a
  286. string giving the verbatim contents expected in the
  287. file (or files). If the files do not match cnt byte
  288. for byte, an exception will be thrown.
  289. add_symlink(f, target): Adds to the test the criterion that upon
  290. successful exit of mcell, the file (or files) f must
  291. exist and be a symlink. 'f' must either be a string
  292. giving a single filename, or an iterable (list, tuple,
  293. etc.) giving a collection of filenames. target is an
  294. optional string giving a required target for the
  295. symlinks. (The target must match verbatim, rather
  296. than simply referring to the same file.) If file is a
  297. collection, target must either be None (unspecified)
  298. or must be a collection, and they will be paired off:
  299. mt.add_symlink(["foobar1.lnk", "foobar2.lnk"],
  300. ["./dat/foobar1.orig", "./dat/foobar2.orig"])
  301. add_extra_check(c): Adds an extra check to this test to be run upon
  302. completion. 'c' must be an object with a 'check()'
  303. method that can be called which throws an exception if
  304. the test fails. Most typically, the objects passed to
  305. add_extra_check will be classes whose names begin with
  306. Require, such as the various utility classes defined
  307. in this file.
  308. invoke(testdir): Invokes this mcell test, creating a test directory
  309. under testdir. The test directory will be
  310. sequentially named test-xxxx where xxxx is a 4-digit
  311. decimal integer. Generally, 'get_output_dir()' should
  312. be passed to this:
  313. mt = MCellTest("foobar", "mdl_does_not_exist.mdl", [])
  314. mt.set_expected_exit_code(1)
  315. mt.invoke(get_output_dir())
  316. Invoke will throw an exception if anything is amiss.
  317. check_output_files(): This may be overridden in McellTest subclasses
  318. to insert custom logic as an alternative to writing a
  319. Require class and adding it using add_extra_check. If
  320. you do override this, be sure to call the parent
  321. class' version of this method:
  322. def check_output_files(self):
  323. McellTest.check_output_files(self)
  324. # now, add other tests here...
  325. =========================================
  326. 3. Specific test types
  327. =========================================
  328. reaction_output:
  329. All of these utilities may be imported from reaction_output. See
  330. system_tests/reaction_output.py for more details. Each test utility in
  331. that file includes a block comment explaining the use of the test and
  332. giving a simple example usage.
  333. ------------
  334. Counts (exact):
  335. ------------
  336. assertCounts
  337. RequireCounts
  338. This test type may be used when the counts must exactly match a given
  339. time course. This almost never happens, but may happen in a few cases,
  340. for constant counts or non-reacting molecules. Most cases are better
  341. handled with one of the other count mechanisms.
  342. In addition to validating the values, this test also validates that the
  343. data is well-formed -- that is, that it has the right number of rows and
  344. columns, and that it has a header if one is expected and does not have a
  345. header if not.
  346. The parameters to this type of count validation are:
  347. fname: the name of the reaction data output file
  348. times_vals: a list of the exact rows expected in the file. Each
  349. item in the list should be a tuple of values. For
  350. instance, in a file counting a constant quantity, such
  351. as produced by the count statement:
  352. REACTION_DATA_OUTPUT
  353. {
  354. STEP = 1e-6
  355. {5} => "output.txt"
  356. }
  357. in a run that ran for 100 iterations with a time step of
  358. 1e-6, times_vals might be set to:
  359. [(f*1e-6, 5) for f in range(0, 101)]
  360. header (optional): a header to expect on the data. If provided, it
  361. must exactly match the first line of input. If not
  362. provided, all lines in the file are expected to be count
  363. data.
  364. eps (optional): an epsilon for what is considered equality.
  365. Defaults to 1e-8.
  366. As with the generic utilities, this may be called either as:
  367. assertCounts(fname, times_vals, header=None, eps=1e-8)
  368. or with the delayed form:
  369. r = RequireCounts(fname, times_vals, header=None, eps=None)
  370. mt.add_extra_check(r)
  371. mt.invoke(get_output_dir())
  372. ------------
  373. Counts (linear constraints):
  374. ------------
  375. assertCountConstraints
  376. RequireCountConstraints
  377. This test type is fairly flexible, and was already briefly described in
  378. the README file. Essentially, you must provide a set of coefficients
  379. which, when multiplied by the columns in each row (optionally restricted
  380. to a given interval of time), will yield a constant value. You may
  381. provide several such linear constraints. The coefficients for all of
  382. the constraints are specified together in a big matrix, and the constant
  383. values to which they must evaluate are specified together in a list.
  384. Having said that, here are the parameters that may be specified:
  385. fname: filename to validate
  386. constraints (opt) : coefficients matrix
  387. totals (opt): result vector
  388. min_time (opt): time at which to start validating these constraints
  389. max_time (opt): time at which to stop validating these constraints
  390. header (opt): If None or False, no header may appear on the data; if
  391. True, a non-empty header must appear on the file; if a
  392. string value is provided, the header must exactly
  393. match the value (except for leading and trailing
  394. whitespace).
  395. num_vals (opt): The number of expected values within the window of
  396. time. If specified, there must be exactly this many
  397. rows between min_time and max_time (or in the whole
  398. file except the header if min_time and max_time are
  399. not specified)
  400. minimums (opt): the minimum values which may appear in each column.
  401. Presently, this value is only heeded if constraints
  402. were specified, but that is a bug. By default, we
  403. assume that no value may be less than 0.
  404. maximums (opt): the maximum values which may appear in each column.
  405. Presently, this value is only heeded if constraints
  406. were specified, but that is a bug. By default, we
  407. assume that no value may be greater than 1e300.
  408. As with the generic utilities, this may be called either as:
  409. assertCountConstraints(fname,
  410. constraints=None,
  411. totals=None,
  412. min_time=None,
  413. max_time=None,
  414. header=None,
  415. num_vals=None,
  416. minimums=None,
  417. maximums=None)
  418. or with the delayed form:
  419. r = RequireCountConstraints(fname,
  420. constraints=None,
  421. totals=None,
  422. min_time=None,
  423. max_time=None,
  424. header=None,
  425. num_vals=None,
  426. minimums=None,
  427. maximums=None)
  428. mt.add_extra_check(r)
  429. mt.invoke(get_output_dir())
  430. ------------
  431. Counts (equilibrium):
  432. ------------
  433. assertCountEquilibrium
  434. RequireCountEquilibrium
  435. There are two ways one could imagine statstically comparing count values
  436. to an expected equilibrium. One would be to repeatedly run the same
  437. simulation and average the same time value across multiple seeds. The
  438. second would be to average the same count across a range of time values
  439. in a single run. Obviously, this only works if the time values are all
  440. in a period of statistical equilibrium. This comparison implements the
  441. second strategy. A minimum and/or maximum time may be provided to limit
  442. the summation to a region which is expected to be in equilibrium, and
  443. expected equilibrium values may be provided (along with tolerances) for
  444. each column. The test is considered to succeed if the average value of
  445. each column is within the given tolerance of each expected equilibrium
  446. value.
  447. fname: filename to validate
  448. values: expected equilibria for each column (list of floats)
  449. tolerances: allowable tolerances for equilibria
  450. min_time (opt): minimum time value at which to start averaging
  451. max_time (opt): maximum time value at which to stop averaging
  452. header (opt): If None or False, no header may appear on the data; if
  453. True, a non-empty header must appear on the file; if a
  454. string value is provided, the header must exactly
  455. match the value (except for leading and trailing
  456. whitespace).
  457. num_vals (opt): The number of expected values within the window of
  458. time. If specified, there must be exactly this many
  459. rows between min_time and max_time (or in the whole
  460. file except the header if min_time and max_time are
  461. not specified)
  462. As with the generic utilities, this may be called either as:
  463. assertCountEquilibrium(fname,
  464. values,
  465. tolerances,
  466. min_time=None,
  467. max_time=None,
  468. header=None,
  469. num_vals=None)
  470. or with the delayed form:
  471. r = RequireCountEquilibrium(fname,
  472. values,
  473. tolerances,
  474. min_time=None,
  475. max_time=None,
  476. header=None,
  477. num_vals=None)
  478. mt.add_extra_check(r)
  479. mt.invoke(get_output_dir())
  480. ------------
  481. Counts (average event rate):
  482. ------------
  483. assertCountRxnRate
  484. RequireCountRxnRate
  485. When a reaction is at equilibrium, the forward and reverse reactions
  486. should proceed at roughly equal rates. We can compute a number of
  487. expected events per iteration, and then validate that the average rate
  488. of occurrence of the reaction is within epsilon of the expected rate.
  489. The current cases for this start the reactions at equilibrium, and a few
  490. modifications will be needed to this if we want to use this on reactions
  491. which do not start at equilibrium. The computation we are doing is:
  492. raw_count / (time - base_time)
  493. where base_time is the time at which the reactions began occurring, by
  494. default 0. To make it work with a system which doesn't start at
  495. equilibrium, we should also specify an equilibrium time, and subtract
  496. the reaction count at the equilibrium time:
  497. (raw_count - counts[eq_time]) / (time - eq_time)
  498. Anyway, even for a system which starts at equilibrium, this is not
  499. immediately accurate -- we need to allow enough time to pass for several
  500. events to occur so that we get enough samples to account for the noise.
  501. fname: filename to validate
  502. values: expected event rate in events per microsecond for each column
  503. tolerances: tolerances for average rates for each column
  504. min_time (opt): minimum time value at which to start averaging
  505. max_time (opt): maximum time value at which to stop averaging
  506. header (opt): If None or False, no header may appear on the data; if
  507. True, a non-empty header must appear on the file; if a
  508. string value is provided, the header must exactly
  509. match the value (except for leading and trailing
  510. whitespace).
  511. As with the generic utilities, this may be called either as:
  512. assertCountRxnRate(fname,
  513. values,
  514. tolerances,
  515. min_time=None,
  516. max_time=None,
  517. header=None)
  518. or with the delayed form:
  519. r = RequireCountRxnRate(fname,
  520. values,
  521. tolerances,
  522. min_time=None,
  523. max_time=None,
  524. header=None)
  525. mt.add_extra_check(r)
  526. mt.invoke(get_output_dir())
  527. ------------
  528. Triggers:
  529. ------------
  530. assertValidTriggerOutput
  531. RequireValidTriggerOutput
  532. Trigger output may be checked for format, and to a certain degree, for
  533. content. Triggers are currently checked for mechanical constraints, such
  534. as syntax (header, if header is expected, correct number of columns) and
  535. spatial localization. In particular, you may specify the bounding box
  536. for a region of space within which all trigger hits in a particular file
  537. are expected to occur; any triggers which occur outside of this region
  538. will cause a test failure.
  539. fname: filename for trigger output
  540. data_cols: 0 for reaction output, 1 for hits, 2 for molecule counts
  541. exact_time: True if there should be an exact time column
  542. header: Expected header line, or None if no header is expected
  543. event_titles: collection of acceptable event titles if triggers
  544. should include event titles, or None if they should not
  545. have titles.
  546. itertime: the length of an iteration (only checked if exact_time is
  547. True). exact_time column is required to be within itertime
  548. of the time column in each row
  549. xrange: a tuple of (xmin, xmax), or None to skip checking in the x
  550. dimension. An error will be signalled if any trigger in this
  551. file occurs with x coordinate outside of the closed interval
  552. [xmin, xmax].
  553. yrange: a tuple of (ymin, ymax), or None to skip checking in the y
  554. dimension. An error will be signalled if any trigger in this
  555. file occurs with y coordinate outside of the closed interval
  556. [ymin, ymax].
  557. zrange: a tuple of (zmin, zmax), or None to skip checking in the z
  558. dimension. An error will be signalled if any trigger in this
  559. file occurs with z coordinate outside of the closed interval
  560. [zmin, zmax].
  561. assertValidTriggerOutput(fname,
  562. data_cols,
  563. exact_time=False,
  564. header=None,
  565. event_titles=False,
  566. itertime=1e-6,
  567. xrange=None,
  568. yrange=None,
  569. zrange=None)
  570. or with the delayed form:
  571. r = RequireValidTriggerOutput(fname,
  572. data_cols,
  573. exact_time=False,
  574. header=None,
  575. event_titles=False,
  576. itertime=1e-6,
  577. xrange=None,
  578. yrange=None,
  579. zrange=None)
  580. mt.add_extra_check(r)
  581. mt.invoke(get_output_dir())
  582. -----------
  583. viz_output:
  584. All of these utilities may be imported from viz_output. See
  585. system_tests/viz_output.py for more details. Each test utility in that
  586. file includes a block comment explaining the use of the test and giving a
  587. simple example usage.
  588. ------------
  589. ASCII mode viz output:
  590. ------------
  591. assertValidVizFileAscii
  592. RequireVizAscii
  593. ASCII viz output will have 7 columns. The first column is always a
  594. state value. Columns 2-4 are the x, y, z coordinates of a molecule.
  595. Columns 5-7 are the normal vector for the molecule, or 0, 0, 0 if the
  596. molecule is a volume molecule. This check will validate that the
  597. column has the right number of lines, and if state values are provided
  598. as parameters, will verify that only the expected states occur, and
  599. that volume molecules correspond only to legal states for volume
  600. molecules and surface molecules correspond only to legal states for
  601. surface molecules. It will check that the state is an integer and that
  602. the other 6 columns are floating point values. It does not presently,
  603. though it could, check that the normal vector, if it is not 0, has
  604. length 1. Unlike most of the tests seen so far,
  605. RequireValidVizFileAscii works slightly differently from
  606. assertValidVizFileAscii. assertValidVizFileAscii validates a single
  607. output file, but RequireValidVizFileAscii will validate the input file
  608. for each iteration number that is expected to produce output.
  609. assertValidVizFileAscii
  610. fname: filename to check
  611. sstates: list of legal surface molecule states (integer values)
  612. vstates: list of legal volume molecule states (integer values)
  613. if both sstates and vstates are None, states will not be
  614. checked; if only one of them is None, it is counted as an
  615. empty list -- i.e. sstates=None means no surface molecules
  616. should appear in the output
  617. RequireVizAscii
  618. basename: specified basename from MDL file for output
  619. (.ascii.<iter>.dat will be appended to get the actual
  620. filename)
  621. iters: iterations which are expected to produce ASCII mode output
  622. sstates: list of legal surface molecule states (as in
  623. assertValidVizFileAscii)
  624. vstates: list of legal volume molecule states (as in
  625. assertValidVizFileAscii)
  626. astates: list of legal states for both surface and volume
  627. molecules. If astates is not None, it is concatenated
  628. to sstates and to vstates.
  629. It is recommended to use the Require* form of the ASCII viz output test:
  630. r = RequireVizAscii(basename,
  631. iters, # for instance: range(0, 1000, step=100),
  632. sstates=None,
  633. vstates=None,
  634. astates=None)
  635. mt.add_extra_check(r)
  636. mt.invoke(get_output_dir())
  637. ------------
  638. RK mode viz output:
  639. ------------
  640. assertValidVizFileRK
  641. RequireVizRK
  642. Only minimal checking is done for Rex's custom output format.
  643. Essentially, we only check that the file exists and (optionally) that it
  644. has the expected number of lines.
  645. fname: filename
  646. n_iters: number of expected (non-blank) lines in the file
  647. As with most tests, this may be invoked either immediately:
  648. assertValidVizFileRK(fname, n_iters=None)
  649. or in delayed form:
  650. r = RequireVizRK(name
  651. n_iters=None)
  652. mt.add_extra_check(r)
  653. mt.invoke(get_output_dir())
  654. ------------
  655. DX mode viz output:
  656. ------------
  657. assertValidVizFilesDx
  658. RequireVizDx
  659. DX output is too complicated to thoroughly validate in an automated way,
  660. at least at present. We can, however, validate at least that the
  661. expected files were all created. It could, but does not presently
  662. validate that only the expected files are created (i.e. that output
  663. wasn't produced for iterations where we didn't expect it). We also check
  664. that the files are non-empty. (XXX: This may not be the correct thing to
  665. do?)
  666. dir: directory in which to look for the DX output
  667. molfile: MOLECULE_FILE_PREFIX if it was specified
  668. objprefixes: list of names in the OBJECT_FILE_PREFIXES if it was
  669. specified
  670. alliters: list of iterations where we expect all types of output
  671. mpositers: list of iterations where we expect volume molecule position
  672. output
  673. mstateiters: list of iterations where we expect volume molecule state
  674. output
  675. epositers: list of iterations where we expect grid molecule position
  676. output
  677. estateiters: list of iterations where we expect grid molecule state
  678. output
  679. opositers: list of iterations where we expect mesh position output
  680. ostateiters: list of iterations where we expect mesh state output
  681. As with most tests, this may be invoked either immediately:
  682. assertValidVizFileDX(dir,
  683. molfile=None,
  684. objprefixes=None,
  685. alliters=None,
  686. mpositers=None,
  687. mstateiters=None,
  688. epositers=None,
  689. estateiters=None,
  690. opositers=None,
  691. ostateiters=None)
  692. or in delayed form:
  693. r = RequireVizDX(dir,
  694. molfile=None,
  695. objprefixes=None,
  696. alliters=None,
  697. mpositers=None,
  698. mstateiters=None,
  699. epositers=None,
  700. estateiters=None,
  701. opositers=None,
  702. ostateiters=None)
  703. mt.add_extra_check(r)
  704. mt.invoke(get_output_dir())
  705. ------------
  706. DREAMM V3 non-grouped viz output:
  707. ------------
  708. assertValidVizFilesDreammV3
  709. RequireVizDreammV3
  710. This validates some basic structural constraints about a non-grouped
  711. DREAMM V3 output. Essentially, it validates the existence and
  712. (optionally) the size of the .iteration_numbers.bin and .time_values.bin
  713. files and the existence of the top-level .dx file. In order to do these
  714. checks, it needs to know the DREAMM output directory, the output filename
  715. (from which the top-level DX file takes its name), and optionally, the
  716. number of distinct output iterations and timesteps.
  717. dir: output directory
  718. name: output set name
  719. n_iters: number of distinct output iterations
  720. n_times: number of distinct output times
  721. As with most tests, this may be invoked either immediately:
  722. assertValidVizFilesDreammV3(dir,
  723. name,
  724. n_iters=None,
  725. n_times=None)
  726. or in delayed form:
  727. r = RequireVizDreammV3(dir,
  728. name,
  729. n_iters=None,
  730. n_times=None)
  731. mt.add_extra_check(r)
  732. mt.invoke(get_output_dir())
  733. ------------
  734. DREAMM V3 non-grouped viz output - binary mode molecule data:
  735. ------------
  736. assertValidVizFilesDreammV3MolsBin
  737. RequireVizDreammV3MolsBin
  738. In a DREAMM V3 output set which should contain binary mode molecule data,
  739. we can validate the existence of appropriate output files for each
  740. iteration. This is a considerably more complex validation that the ones
  741. described thus far.
  742. dir: the DREAMM output directory
  743. alliters: sorted list of iterations where output is expected
  744. surfpositers: list of iters where surface molecule position output is
  745. expected
  746. surforientiters: list of iters where surface molecule orientation
  747. output is expected
  748. surfstateiters: list of iters where surface molecule state output is
  749. expected
  750. surfnonempty: flag indicating that at least one surface molecule should
  751. have been output
  752. volpositers: list of iters where volume molecule position output is
  753. expected
  754. volorientiters: list of iters where volume molecule orientation output
  755. is expected
  756. volstateiters: list of iters where volume molecule state output is
  757. expected
  758. volnonempty: flag indicating that at least one volume molecule should
  759. have been output
  760. Each of the iters lists may be set to None which indicates that the
  761. output is expected on every iteration in alliters. To disable the
  762. check for a particular type of output, set its iters list to [] (the
  763. empty list). On iterations where a particular type of output should
  764. not be output, but which are subsequent to an iteration where that type
  765. of output has been output, we will check for appropriate symbolic links.
  766. As with most tests, this may be invoked either immediately:
  767. assertValidVizFilesDreammV3MolsBin(dir,
  768. alliters,
  769. surfpositers=None,
  770. surforientiters=None,
  771. surfstateiters=[],
  772. surfnonempty=True,
  773. volpositers=None,
  774. volorientiters=None,
  775. volstateiters=[],
  776. volnonempty=True)
  777. or in delayed form:
  778. r = RequireVizDreammV3MolsBin(dir,
  779. alliters,
  780. surfpositers=None,
  781. surforientiters=None,
  782. surfstateiters=[],
  783. surfnonempty=True,
  784. volpositers=None,
  785. volorientiters=None,
  786. volstateiters=[],
  787. volnonempty=True)
  788. mt.add_extra_check(r)
  789. mt.invoke(get_output_dir())
  790. ------------
  791. DREAMM V3 non-grouped viz output - ASCII mode molecule data:
  792. ------------
  793. assertValidVizFilesDreammV3MolsAscii
  794. RequireVizDreammV3MolsAscii
  795. In a DREAMM V3 output set which should contain ASCII mode molecule data,
  796. we can validate the existence of appropriate output files for each
  797. iteration.
  798. dir: the DREAMM output directory
  799. alliters: all iterations where output is expected
  800. molnames: names of molecules for which output is expected
  801. positers: iterations where position output is expected (or None for
  802. "all")
  803. orientiters: iterations where orientation output is expected (or None
  804. for "all")
  805. stateiters: iterations where state output is expected (or None for
  806. "all")
  807. As with most tests, this may be invoked either immediately:
  808. assertValidVizFilesDreammV3MolsAscii(dir,
  809. alliters,
  810. molnames,
  811. positers=None,
  812. orientiters=None,
  813. stateiters=[])
  814. or in delayed form:
  815. r = RequireVizDreammV3MolsAscii(dir,
  816. alliters,
  817. molnames,
  818. positers=None,
  819. orientiters=None,
  820. stateiters=[])
  821. mt.add_extra_check(r)
  822. mt.invoke(get_output_dir())
  823. ------------
  824. DREAMM V3 non-grouped viz output - binary mode mesh data:
  825. ------------
  826. assertValidVizFilesDreammV3MeshBin
  827. RequireVizDreammV3MeshBin
  828. For DREAMM output sets which should include at binary mode mesh data, we
  829. can verify the existence of the appropriate files and symlinks, and can
  830. optionally validate that at least one mesh was output.
  831. dir: the DREAMM output directory
  832. alliters: sorted list of iterations where output is expected
  833. positers: iteration numbers where mesh position output was produced
  834. (None for all iterations in alliters)
  835. regioniters: iteration numbers where region data output was produced
  836. (None for all iterations in alliters)
  837. stateiters: iteration numbers where mesh state output was produced
  838. (None for all iterations in alliters)
  839. meshnonempty: if true, we will check that at least one mesh was output
  840. As with most tests, this may be invoked either immediately:
  841. assertValidVizFilesDreammV3MeshBin(dir,
  842. alliters,
  843. positers=None,
  844. regioniters=None,
  845. stateiters=[],
  846. meshnonempty=True)
  847. or in delayed form:
  848. r = RegionVizDreammV3MeshBin(dir,
  849. alliters,
  850. positers=None,
  851. regioniters=None,
  852. stateiters=[],
  853. meshnonempty=True)
  854. mt.add_extra_check(r)
  855. mt.invoke(get_output_dir())
  856. ------------
  857. DREAMM V3 non-grouped viz output - ASCII mode mesh data:
  858. ------------
  859. assertValidVizFilesDreammV3MeshAscii
  860. RequireVizDreammV3MeshAscii
  861. For DREAMM output sets which should include at ASCII mode mesh data, we
  862. can verify the existence of the appropriate files and symlinks, and can
  863. optionally validate that at least one mesh was output.
  864. dir: the DREAMM output directory
  865. alliters: sorted list of iterations where output is expected
  866. objnames: names of objects for which output should be produced
  867. objswithregions: names of objects which have defined regions (other
  868. than ALL)
  869. positers: iteration numbers where mesh position output was produced
  870. (None for all iterations in alliters)
  871. regioniters: iteration numbers where region data output was produced
  872. (None for all iterations in alliters)
  873. stateiters: iteration numbers where mesh state output was produced
  874. (None for all iterations in alliters)
  875. meshnonempty: if true, we will check that at least one mesh was output
  876. As with most tests, this may be invoked either immediately:
  877. assertValidVizFilesDreammV3MeshAscii(dir,
  878. alliters,
  879. objnames,
  880. objswithregions=None,
  881. positers=None,
  882. regioniters=None,
  883. stateiters=[],
  884. meshnonempty=True)
  885. or in delayed form:
  886. r = RegionVizDreammV3MeshAscii(dir,
  887. alliters,
  888. objnames,
  889. objswithregions=None,
  890. positers=None,
  891. regioniters=None,
  892. stateiters=[],
  893. meshnonempty=True)
  894. mt.add_extra_check(r)
  895. mt.invoke(get_output_dir())
  896. ------------
  897. DREAMM V3 grouped viz output:
  898. ------------
  899. assertValidVizFilesDreammV3Grouped
  900. RequireVizDreammV3Grouped
  901. A DREAMM V3 grouped file is best validated all-at-once. Less validation
  902. may be done than in the ungrouped case because less of the state is
  903. directly accessible from the filesystem (i.e. without parsing DX files.)
  904. Still, a reasonable amount of validation can be done.
  905. dir: DREAMM output directory
  906. name: output set name
  907. cpno: checkpoint sequence number (defaults to 1)
  908. n_iters: total number of iterations (None to skip iteration count check)
  909. n_times: total number of distinct time points (None to skip time count
  910. check)
  911. meshpos: True iff we should expect mesh positions output
  912. rgnindx: True iff we should expect region indices output
  913. meshstate: True iff we should expect mesh states output
  914. meshnonempty: True if we should expect at least one mesh output
  915. molpos: True iff we should expect molecule position output
  916. molorient: True iff we should expect molecule orientation output
  917. molstate: True iff we should expect molecule state output
  918. molsnonempty: True if we should expect at least one molecule output
  919. As with the other test types, this may be run in immediate form:
  920. assertValidVizFilesDreammV3Grouped(dir,
  921. name,
  922. cpno=1,
  923. n_iters=None,
  924. n_times=None,
  925. meshpos=True,
  926. rgnindex=True,
  927. meshstate=False,
  928. meshnonempty=True,
  929. molpos=True,
  930. molorient=True,
  931. molstate=False,
  932. molsnonempty=True)
  933. or in delayed form:
  934. r = RequireVizDreammV3Grouped(dir,
  935. name,
  936. cpno=1,
  937. n_iters=None,
  938. n_times=None,
  939. meshpos=True,
  940. rgnindex=True,
  941. meshstate=False,
  942. meshnonempty=True,
  943. molpos=True,
  944. molorient=True,
  945. molstate=False,
  946. molsnonempty=True)
  947. mt.add_extra_check(r)
  948. mt.invoke(get_output_dir())
  949. =========================================
  950. 4. Test-specific utilities
  951. =========================================
  952. I'm not actually going to explain how to use the existing test-specific
  953. utilities, but I am going to mention them as examples of how to write more
  954. complicated tests. Many of the parser tests involve similar checks and
  955. setup, and as a result, special base classes have been created in the
  956. parser_test_types.py module in mdl/testsuite/parser. This file is
  957. well-commented and illustrates the use of several of the test types I've
  958. mentioned here.
  959. test_parser.py includes a useful trick for batch-generating tests, which
  960. may be of interest if you need to add a large number of very similar tests.
  961. In this case, the tests are for ~80 tests for parsing malformed MDL files:
  962. class TestParseInvalid(unittest.TestCase):
  963. def test025(self):
  964. InvalidParserTest("invalid-025.mdl").add_empty_file("invalid-025.tmp").invoke(get_output_dir())
  965. def make_invalid_test(i):
  966. methname = "test%03d" % i
  967. filename = "invalid-%03d.mdl" % i
  968. func = lambda self: InvalidParserTest(filename).invoke(get_output_dir())
  969. setattr(TestParseInvalid, methname, func)
  970. ## Bulk generate invalid test cases 1...23, 26...27, 29...85
  971. ## 25 is specially specified, and 24 and 28 do not presently exist.
  972. for i in crange(1, 23) + crange(26, 27) + crange(29, 85):
  973. make_invalid_test(i)
  974. Essentially, I created a test case with whatever custom tests I needed, and
  975. then wrote a loop to generate the other methods, which were formulaic and
  976. merely involved creating the appropriate test type with a formulaic mdl
  977. filename. The rest of the logic is contained in the InvalidParserTest
  978. class which may be seen in parser_test_types.py.
  979. I added a similar macromol_test_types.py in the macromols subdirectory, but
  980. it contains very little of practical interest aside from an analogue of the
  981. InvalidParserTest class for catching macromolecule parser errors. There
  982. are, however, a few utilities in test_macromols.py that may be of interest,
  983. as they show how to create custom check types to be added with
  984. 'add_extra_check'. In particular, CheckListReleasePositions is used to
  985. validate the placement locations of the macromolecule subunits based on
  986. their list placement. This particular example is specific to one of the
  987. macromolecule tests, and I guess I must have decided it wasn't generic
  988. enough to merit inclusion in macromol_test_types? test_macromols.py also
  989. includes a fairly frightening example of the use of RequireCountConstraints
  990. to validate the macromolecule counting code (see the test_surface method).