utils.py 10 KB

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