utils.py 11 KB

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