utils.py 11 KB

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