utils.py 11 KB

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