utils.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  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. from hutn_compiler.compiler import main as do_compile
  14. username = "test_user"
  15. parallel_push = True
  16. ports = []
  17. def getFreePort():
  18. while 1:
  19. port = random.randint(10000, 20000)
  20. ports.append(port)
  21. exists = False
  22. for p in ports:
  23. if p == port:
  24. if not exists:
  25. # We have hopefully found our own
  26. exists = True
  27. else:
  28. # We seem to be the second entry, so chose another one
  29. ports.remove(port)
  30. break
  31. else:
  32. # Didn't find a duplicate
  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), "username": username})), 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. username = str(random.random())
  58. while 1:
  59. proc2 = execute("compile", [address, mod_filename, username, 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, mode):
  85. # Resolve file
  86. import os.path
  87. time.sleep(0.01)
  88. port = getFreePort()
  89. address = "http://127.0.0.1:%i" % port
  90. try:
  91. # Run Modelverse server
  92. proc = execute("run_local_modelverse", [str(port)], wait=False)
  93. threads = []
  94. for filename in files:
  95. if os.path.isfile("integration/code/%s" % filename):
  96. mod_filename = "integration/code/%s" % filename
  97. elif os.path.isfile("bootstrap/%s" % filename):
  98. mod_filename = "bootstrap/%s" % filename
  99. else:
  100. raise Exception("File not found: %s" % filename)
  101. print("Found file " + str(mod_filename))
  102. if parallel_push:
  103. import threading
  104. threads.append(threading.Thread(target=compile_file, args=[address, mod_filename, filename, mode, proc]))
  105. threads[-1].start()
  106. else:
  107. compile_file(address, mod_filename, filename, mode, proc)
  108. # After the first file, which can be whatever we want, we just have to compile dependencies, all of which are PO for efficiency
  109. mode = "PO"
  110. if parallel_push:
  111. for t in threads:
  112. t.join()
  113. if mode[-1] == "O":
  114. # Fire up the linker
  115. val = execute("link_and_load", [address, username] + files, wait=True)
  116. if val != 0:
  117. raise Exception("Linking error")
  118. # Send in the actual request and wait for replies
  119. var_list = {}
  120. data = []
  121. got_output = []
  122. for p in parameters:
  123. if isinstance(p, int):
  124. if p not in var_list:
  125. data = flush_data(address, data)
  126. proc.poll()
  127. if proc.returncode is not None:
  128. # Modelverse has already terminated, which isn't a good sign!
  129. return False
  130. val = urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "get_output", "username": username})), timeout=10).read()
  131. #TODO if there is a value here, we have to store it for later use!
  132. l, r = val.split("&", 1)
  133. if l.startswith("id"):
  134. id_str = l
  135. val_str = r
  136. else:
  137. id_str = r
  138. val_str = l
  139. id_val = id_str.split("=", 1)[1]
  140. val_val = val_str.split("=", 1)[1]
  141. #TODO this doesn't work!!! we expect it to be filled in, which it isn't!
  142. if val_val == "None":
  143. print("Value is for us: keeping")
  144. print(id_val)
  145. print(val_val)
  146. var_list[p] = id_val
  147. else:
  148. print("Value not for us: skip")
  149. print(id_val)
  150. print(val_val)
  151. got_output.append(val)
  152. continue
  153. else:
  154. val = var_list[p]
  155. t = "R"
  156. else:
  157. val = p
  158. t = "V"
  159. data.append([t, val])
  160. data = flush_data(address, data)
  161. flush_data(address, data)
  162. for e in expected:
  163. c = len(e) if isinstance(e, set) else 1
  164. for _ in range(c):
  165. if got_output:
  166. val = got_output.pop(0)
  167. else:
  168. val = urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "get_output", "username": username})), timeout=240).read()
  169. if proc.returncode is not None:
  170. # Modelverse has already terminated, which isn't a good sign!
  171. raise Exception("Modelverse died!")
  172. val = val.split("=", 2)[2]
  173. print("Got %s, expect %s" % (val, e))
  174. if isinstance(e, set):
  175. assert str(val) in e
  176. if str(val) not in e:
  177. return False
  178. else:
  179. assert str(val) == e
  180. if str(val) != e:
  181. return False
  182. # All passed!
  183. return True
  184. except:
  185. raise
  186. finally:
  187. try:
  188. kill(proc)
  189. except UnboundLocalError:
  190. pass
  191. def run_barebone(parameters, expected, interface="0", timeout=False, wait=False, link=None, inputs=[]):
  192. port = getFreePort()
  193. address = "http://127.0.0.1:%i" % port
  194. try:
  195. # Run Modelverse server
  196. proc = execute("run_local_modelverse", [str(port)], wait=False)
  197. # Create user and set interface
  198. timeout_val = 15
  199. start = time.time()
  200. while 1:
  201. proc.poll()
  202. if proc.returncode is not None:
  203. # Modelverse has already terminated, which isn't a good sign!
  204. return False
  205. try:
  206. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "element_type": "V", "value": '"%s"' % username, "username": "user_manager"})), timeout=1).read()
  207. if interface is not None:
  208. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "element_type": "V", "value": interface, "username": username})), timeout=1).read()
  209. break
  210. except:
  211. time.sleep(0.01)
  212. if time.time() - start > timeout_val:
  213. raise
  214. # Send in the actual request and wait for replies
  215. var_list = {}
  216. data = []
  217. for p in parameters:
  218. if isinstance(p, int):
  219. if p not in var_list:
  220. data = flush_data(address, data)
  221. proc.poll()
  222. if proc.returncode is not None:
  223. # Modelverse has already terminated, which isn't a good sign!
  224. return False
  225. val = urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "get_output", "username": username})), timeout=10).read()
  226. val = val.split("=", 2)[1].split("&", 1)[0]
  227. var_list[p] = val
  228. continue
  229. else:
  230. val = var_list[p]
  231. t = "R"
  232. else:
  233. val = p
  234. t = "V"
  235. data.append([t, val])
  236. data = flush_data(address, data)
  237. # Now do linking and loading
  238. if link is not None:
  239. # Execute linker
  240. timeout_val = 10
  241. proc2 = execute("link_and_load", [address, username] + link, wait=False)
  242. while proc2.returncode is None:
  243. time.sleep(0.01)
  244. proc2.poll()
  245. timeout_val -= 0.01
  246. if timeout_val < 0:
  247. kill(proc2)
  248. print("Linking timeout expired!")
  249. return False
  250. if proc.returncode is not None:
  251. # Modelverse has already terminated, which isn't a good sign!
  252. return False
  253. for inp in inputs:
  254. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "element_type": "V", "value": inp, "username": username})), timeout=1).read()
  255. proc.poll()
  256. if proc.returncode is not None:
  257. # Modelverse has already terminated, which isn't a good sign!
  258. return False
  259. counter = 0
  260. for e in expected:
  261. print("Expect " + str(e))
  262. c = len(e) if isinstance(e, set) else 1
  263. for _ in range(c):
  264. try:
  265. proc.poll()
  266. if proc.returncode is not None:
  267. # Modelverse has already terminated, which isn't a good sign!
  268. return False
  269. val = urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "get_output", "username": username})), timeout=240 if not timeout else 20).read()
  270. except:
  271. if timeout:
  272. return True
  273. else:
  274. raise
  275. val = val.split("=", 2)[2]
  276. print("Got %s, expect %s" % (val, e))
  277. if isinstance(e, set):
  278. assert str(val) in e
  279. if str(val) not in e:
  280. return False
  281. else:
  282. assert str(val) == e
  283. if str(val) != e:
  284. return False
  285. # All passed!
  286. return not timeout
  287. finally:
  288. kill(proc)
  289. def get_constructor(code):
  290. with open("__constraint.al", "w") as f:
  291. f.write(code)
  292. f.flush()
  293. constructors = do_compile("__constraint.al", "interface/HUTN/grammars/actionlanguage.g", "CS")
  294. print("Got constructors: " + str(constructors))
  295. return constructors