utils.py 11 KB

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