utils.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. import unittest
  2. import sys
  3. import os
  4. import sys
  5. import time
  6. import json
  7. import urllib
  8. import urllib2
  9. import subprocess
  10. import signal
  11. import random
  12. import pytest
  13. sys.path.append("interface/HUTN")
  14. sys.path.append("scripts")
  15. from hutn_compiler.compiler import main as do_compile
  16. from check_objects import to_recompile
  17. taskname = "test_task"
  18. parallel_push = True
  19. slow = pytest.mark.skipif(
  20. not pytest.config.getoption("--runslow"),
  21. reason="need --runslow option to run"
  22. )
  23. ports = set()
  24. def getFreePort():
  25. """Gets a unique new port."""
  26. while 1:
  27. port = random.randint(10000, 20000)
  28. # Check if this port is in the set of ports.
  29. if port not in ports:
  30. # We have found a unique port. Add it to the set and return.
  31. ports.add(port)
  32. return port
  33. def execute(scriptname, parameters=[], wait=False):
  34. if os.name not in ["nt", "posix"]:
  35. # Stop now, as we would have no clue on how to kill its subtree
  36. raise Exception("Unknown OS version: " + str(os.name))
  37. command = [sys.executable, "scripts/%s.py" % scriptname] + parameters
  38. if wait:
  39. return subprocess.call(command, shell=False)
  40. else:
  41. return subprocess.Popen(command, shell=False)
  42. def kill(process):
  43. if os.name == "nt":
  44. subprocess.call(["taskkill", "/F", "/T", "/PID", "%i" % process.pid])
  45. elif os.name == "posix":
  46. subprocess.call(["pkill", "-P", "%i" % process.pid])
  47. def flush_data(address, data):
  48. if data:
  49. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "data": json.dumps(data), "taskname": taskname})), timeout=10).read()
  50. return []
  51. def compile_file(address, mod_filename, filename, mode, proc):
  52. # Load in the file required
  53. try:
  54. timeout_val = 240
  55. import random
  56. taskname = str(random.random())
  57. while 1:
  58. proc2 = execute("compile", [address, mod_filename, taskname, filename, mode], wait=False)
  59. if proc.returncode is not None:
  60. # Modelverse has already terminated, which isn't a good sign!
  61. raise Exception("Modelverse died!")
  62. while proc2.returncode is None:
  63. time.sleep(0.01)
  64. proc2.poll()
  65. timeout_val -= 0.01
  66. if timeout_val < 0:
  67. kill(proc2)
  68. print("Compilation timeout expired!")
  69. return False
  70. if proc2.returncode != 2:
  71. break
  72. # Make sure everything stopped correctly
  73. assert proc2.returncode == 0
  74. if proc2.returncode != 0:
  75. return False
  76. except:
  77. raise
  78. finally:
  79. try:
  80. kill(proc2)
  81. except UnboundLocalError:
  82. pass
  83. def run_file(files, parameters, expected, wait=False):
  84. # Resolve file
  85. import os.path
  86. if wait is True:
  87. expected = None
  88. time.sleep(0.01)
  89. port = getFreePort()
  90. address = "http://127.0.0.1:%i" % port
  91. try:
  92. # Run Modelverse server
  93. proc = execute("run_local_modelverse", [str(port)], wait=False)
  94. threads = []
  95. mod_files = []
  96. for filename in files:
  97. if os.path.isfile(filename):
  98. mod_filename = filename
  99. elif os.path.isfile("integration/code/%s" % filename):
  100. mod_filename = "integration/code/%s" % filename
  101. elif os.path.isfile("bootstrap/%s" % filename):
  102. mod_filename = "bootstrap/%s" % filename
  103. else:
  104. raise Exception("File not found: %s" % filename)
  105. mod_files.append(mod_filename)
  106. to_compile = to_recompile(address, mod_files)
  107. for mod_filename in to_compile:
  108. if mod_filename.endswith(".mvc"):
  109. model_mode = "MO"
  110. mod_files.remove(mod_filename)
  111. else:
  112. model_mode = "PO"
  113. if parallel_push:
  114. import threading
  115. threads.append(threading.Thread(target=compile_file, args=[address, mod_filename, mod_filename, model_mode, proc]))
  116. threads[-1].start()
  117. else:
  118. compile_file(address, mod_filename, mod_filename, model_mode, proc)
  119. if parallel_push:
  120. for t in threads:
  121. t.join()
  122. # Fire up the linker
  123. val = execute("link_and_load", [address, taskname] + mod_files, wait=True)
  124. if val != 0:
  125. raise Exception("Linking error")
  126. # Send the request ...
  127. flush_data(address, parameters)
  128. # ... and wait for replies
  129. if expected is None:
  130. while 1:
  131. val = urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "get_output", "taskname": taskname})), timeout=240).read()
  132. val = json.loads(val)
  133. print(val)
  134. for e in expected:
  135. c = len(e) if isinstance(e, set) else 1
  136. for _ in range(c):
  137. val = urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "get_output", "taskname": taskname})), timeout=240).read()
  138. val = json.loads(val)
  139. if proc.returncode is not None:
  140. # Modelverse has already terminated, which isn't a good sign!
  141. raise Exception("Modelverse died!")
  142. print("Got %s, expect %s" % (val, e))
  143. if isinstance(e, set):
  144. assert val in e
  145. if val not in e:
  146. return False
  147. elif e is None:
  148. # Skip output value
  149. pass
  150. else:
  151. assert val == e
  152. if val != e:
  153. return False
  154. # All passed!
  155. return True
  156. except:
  157. raise
  158. finally:
  159. try:
  160. kill(proc)
  161. except UnboundLocalError:
  162. pass
  163. def run_barebone(parameters, expected, interface="0", timeout=False, wait=False, link=None, inputs=[]):
  164. port = getFreePort()
  165. address = "http://127.0.0.1:%i" % port
  166. try:
  167. # Run Modelverse server
  168. proc = execute("run_local_modelverse", [str(port)], wait=False)
  169. # Create task and set interface
  170. timeout_val = 15
  171. start = time.time()
  172. while 1:
  173. proc.poll()
  174. if proc.returncode is not None:
  175. # Modelverse has already terminated, which isn't a good sign!
  176. return False
  177. try:
  178. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "element_type": "V", "value": '"%s"' % taskname, "taskname": "task_manager"})), timeout=1).read()
  179. if interface is not None:
  180. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "element_type": "V", "value": interface, "taskname": taskname})), timeout=1).read()
  181. break
  182. except:
  183. time.sleep(0.01)
  184. if time.time() - start > timeout_val:
  185. raise
  186. # Send the request
  187. flush_data(address, parameters)
  188. # Now do linking and loading
  189. if link is not None:
  190. # Execute linker
  191. timeout_val = 10
  192. proc2 = execute("link_and_load", [address, taskname] + link, wait=False)
  193. while proc2.returncode is None:
  194. time.sleep(0.01)
  195. proc2.poll()
  196. timeout_val -= 0.01
  197. if timeout_val < 0:
  198. kill(proc2)
  199. print("Linking timeout expired!")
  200. return False
  201. if proc.returncode is not None:
  202. # Modelverse has already terminated, which isn't a good sign!
  203. return False
  204. for inp in inputs:
  205. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "element_type": "V", "value": inp, "taskname": taskname})), timeout=1).read()
  206. proc.poll()
  207. if proc.returncode is not None:
  208. # Modelverse has already terminated, which isn't a good sign!
  209. return False
  210. counter = 0
  211. for e in expected:
  212. print("Expect " + str(e))
  213. c = len(e) if isinstance(e, set) else 1
  214. for _ in range(c):
  215. try:
  216. proc.poll()
  217. if proc.returncode is not None:
  218. # Modelverse has already terminated, which isn't a good sign!
  219. return False
  220. val = urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "get_output", "taskname": taskname})), timeout=240 if not timeout else 20).read()
  221. val = json.loads(val)
  222. except:
  223. if timeout:
  224. return True
  225. else:
  226. raise
  227. print("Got %s, expect %s" % (val, e))
  228. if isinstance(e, set):
  229. assert val in e
  230. if val not in e:
  231. return False
  232. elif e is None:
  233. # Skip this input
  234. pass
  235. else:
  236. assert val == e
  237. if val != e:
  238. return False
  239. # All passed!
  240. return not timeout
  241. finally:
  242. kill(proc)
  243. def get_constructor(code):
  244. code_fragments = code.split("\n")
  245. code_fragments = [i for i in code_fragments if i.strip() != ""]
  246. code_fragments = [i.replace(" ", "\t") for i in code_fragments]
  247. initial_tabs = min([len(i) - len(i.lstrip("\t")) for i in code_fragments])
  248. code_fragments = [i[initial_tabs:] for i in code_fragments]
  249. code_fragments.append("")
  250. code = "\n".join(code_fragments)
  251. with open("__constraint.alc", "w") as f:
  252. f.write(code)
  253. f.flush()
  254. constructors = do_compile("__constraint.alc", "interface/HUTN/grammars/actionlanguage.g", "CS")
  255. return constructors
  256. def get_model_constructor(code):
  257. # First change multiple spaces to a tab
  258. code_fragments = code.split("\n")
  259. code_fragments = [i for i in code_fragments if i.strip() != ""]
  260. code_fragments = [i.replace(" ", "\t") for i in code_fragments]
  261. initial_tabs = min([len(i) - len(i.lstrip("\t")) for i in code_fragments])
  262. code_fragments = [i[initial_tabs:] for i in code_fragments]
  263. code_fragments.append("")
  264. code = "\n".join(code_fragments)
  265. with open("__model.mvc", "w") as f:
  266. f.write(code)
  267. f.flush()
  268. constructors = do_compile("__model.mvc", "interface/HUTN/grammars/modelling.g", "M") + ["exit"]
  269. return constructors