mirror of
https://github.com/vgstation-coders/vgstation13.git
synced 2025-12-10 02:16:05 +00:00
@@ -9,8 +9,12 @@ env:
|
|||||||
- BYOND_MAJOR="512"
|
- BYOND_MAJOR="512"
|
||||||
- BYOND_MINOR="1413"
|
- BYOND_MINOR="1413"
|
||||||
- ALL_MAPS="tgstation metaclub defficiency packedstation test_box test_tiny"
|
- ALL_MAPS="tgstation metaclub defficiency packedstation test_box test_tiny"
|
||||||
|
- PROJECT_NAME="vgstation13"
|
||||||
- RUST_BACKTRACE="1"
|
- RUST_BACKTRACE="1"
|
||||||
- RUST_TEST_THREADS=1
|
- RUST_TEST_THREADS=1
|
||||||
|
matrix:
|
||||||
|
- DM_UNIT_TESTS="1"
|
||||||
|
- DM_UNIT_TESTS="0"
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
directories:
|
directories:
|
||||||
@@ -42,7 +46,9 @@ script:
|
|||||||
# --jobs 1 to prevent threading problems with the BYOND crate.
|
# --jobs 1 to prevent threading problems with the BYOND crate.
|
||||||
- cargo test --jobs 1 --verbose
|
- cargo test --jobs 1 --verbose
|
||||||
- cd -
|
- cd -
|
||||||
- echo ${ALL_MAPS} | xargs tools/travis/build.py vgstation13.dme -M
|
- tools/travis/build.py
|
||||||
|
- cp tools/travis/config/config.txt config/
|
||||||
|
- (! tools/travis/run_tests.py | grep -zq "UNIT TEST FAIL")
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
irc:
|
irc:
|
||||||
|
|||||||
@@ -121,6 +121,8 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
|||||||
// Please don't stuff random bullshit here,
|
// Please don't stuff random bullshit here,
|
||||||
// Make a subsystem, give it the SS_NO_FIRE flag, and do your work in it's Initialize()
|
// Make a subsystem, give it the SS_NO_FIRE flag, and do your work in it's Initialize()
|
||||||
/datum/controller/master/proc/Setup()
|
/datum/controller/master/proc/Setup()
|
||||||
|
set waitfor = FALSE
|
||||||
|
sleep(1 SECONDS)
|
||||||
to_chat(world, "<span class='boldannounce'>Initializing subsystems...</span>")
|
to_chat(world, "<span class='boldannounce'>Initializing subsystems...</span>")
|
||||||
|
|
||||||
// Sort subsystems by init_order, so they initialize in the correct order.
|
// Sort subsystems by init_order, so they initialize in the correct order.
|
||||||
@@ -141,7 +143,6 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
|||||||
// Sort subsystems by display setting for easy access.
|
// Sort subsystems by display setting for easy access.
|
||||||
sortTim(subsystems, /proc/cmp_subsystem_display)
|
sortTim(subsystems, /proc/cmp_subsystem_display)
|
||||||
// Set world options.
|
// Set world options.
|
||||||
world.sleep_offline = 1
|
|
||||||
world.tick_lag = config.Ticklag
|
world.tick_lag = config.Ticklag
|
||||||
sleep(1)
|
sleep(1)
|
||||||
// Loop.
|
// Loop.
|
||||||
|
|||||||
@@ -74,7 +74,11 @@ var/datum/controller/gameticker/ticker
|
|||||||
send2maindiscord("**Server is loaded** and in pre-game lobby at `[config.server? "byond://[config.server]" : "byond://[world.address]:[world.port]"]`")
|
send2maindiscord("**Server is loaded** and in pre-game lobby at `[config.server? "byond://[config.server]" : "byond://[world.address]:[world.port]"]`")
|
||||||
|
|
||||||
do
|
do
|
||||||
var/delay_timetotal = 3000 //actually 5 minutes or incase this is changed from 3000, (time_in_seconds * 10)
|
#ifdef UNIT_TESTS
|
||||||
|
var/delay_timetotal = 2 SECONDS
|
||||||
|
#else
|
||||||
|
var/delay_timetotal = 5 MINUTES
|
||||||
|
#endif
|
||||||
pregame_timeleft = world.timeofday + delay_timetotal
|
pregame_timeleft = world.timeofday + delay_timetotal
|
||||||
to_chat(world, "<B><FONT color='blue'>Welcome to the pre-game lobby!</FONT></B>")
|
to_chat(world, "<B><FONT color='blue'>Welcome to the pre-game lobby!</FONT></B>")
|
||||||
to_chat(world, "Please, setup your character and select ready. Game will start in [(pregame_timeleft - world.timeofday) / 10] seconds.")
|
to_chat(world, "Please, setup your character and select ready. Game will start in [(pregame_timeleft - world.timeofday) / 10] seconds.")
|
||||||
@@ -184,6 +188,10 @@ var/datum/controller/gameticker/ticker
|
|||||||
//here to initialize the random events nicely at round start
|
//here to initialize the random events nicely at round start
|
||||||
setup_economy()
|
setup_economy()
|
||||||
|
|
||||||
|
#ifdef UNIT_TESTS
|
||||||
|
run_unit_tests()
|
||||||
|
#endif
|
||||||
|
|
||||||
spawn(0)//Forking here so we dont have to wait for this to finish
|
spawn(0)//Forking here so we dont have to wait for this to finish
|
||||||
mode.post_setup()
|
mode.post_setup()
|
||||||
//Cleanup some stuff
|
//Cleanup some stuff
|
||||||
|
|||||||
@@ -101,7 +101,8 @@
|
|||||||
desclines += " (This error will now be silenced for [configured_error_silence_time / 600] minutes)"
|
desclines += " (This error will now be silenced for [configured_error_silence_time / 600] minutes)"
|
||||||
|
|
||||||
// Now to actually output the error info...
|
// Now to actually output the error info...
|
||||||
world.log << "\[[time_stamp()]] Runtime in [e.file],[e.line]: [e]"
|
var/main_line = "\[[time_stamp()]] Runtime in [e.file],[e.line]: [e]"
|
||||||
|
world.log << main_line
|
||||||
|
|
||||||
for (var/line in desclines)
|
for (var/line in desclines)
|
||||||
world.log << line
|
world.log << line
|
||||||
@@ -109,4 +110,9 @@
|
|||||||
if (global.error_cache)
|
if (global.error_cache)
|
||||||
global.error_cache.log_error(e, desclines)
|
global.error_cache.log_error(e, desclines)
|
||||||
|
|
||||||
|
#ifdef UNIT_TESTS
|
||||||
|
if(global.current_test)
|
||||||
|
global.current_test.fail("[main_line]\n[desclines.Join("\n")]")
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
4
code/modules/unit_tests/_unit_tests.dm
Normal file
4
code/modules/unit_tests/_unit_tests.dm
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#ifdef UNIT_TESTS
|
||||||
|
#include "unit_test.dm"
|
||||||
|
#include "reagent_recipe_collisions.dm"
|
||||||
|
#endif
|
||||||
67
code/modules/unit_tests/reagent_recipe_collisions.dm
Normal file
67
code/modules/unit_tests/reagent_recipe_collisions.dm
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/datum/unit_test/reagent_recipe_collisions
|
||||||
|
|
||||||
|
/datum/unit_test/reagent_recipe_collisions/start()
|
||||||
|
var/datum/reagents/r = new // Builds chemical_reactions_list
|
||||||
|
qdel(r)
|
||||||
|
r = null
|
||||||
|
var/list/reactions = list()
|
||||||
|
for(var/V in global.chemical_reactions_list)
|
||||||
|
reactions += global.chemical_reactions_list[V]
|
||||||
|
for(var/i in 1 to (reactions.len-1))
|
||||||
|
for(var/i2 in (i+1) to reactions.len)
|
||||||
|
var/datum/chemical_reaction/r1 = reactions[i]
|
||||||
|
var/datum/chemical_reaction/r2 = reactions[i2]
|
||||||
|
if(recipes_do_conflict(r1, r2))
|
||||||
|
fail("Chemical recipe conflict between [r1.type] and [r2.type]")
|
||||||
|
|
||||||
|
/datum/unit_test/reagent_recipe_collisions/proc/recipes_do_conflict(datum/chemical_reaction/r1, datum/chemical_reaction/r2)
|
||||||
|
//do the non-list tests first, because they are cheaper
|
||||||
|
if(r1.required_container != r2.required_container)
|
||||||
|
return FALSE
|
||||||
|
if(r1.is_cold_recipe == r2.is_cold_recipe)
|
||||||
|
if(r1.required_temp != r2.required_temp)
|
||||||
|
//one reaction requires a more extreme temperature than the other, so there is no conflict
|
||||||
|
return FALSE
|
||||||
|
else
|
||||||
|
var/datum/chemical_reaction/cold_one = r1.is_cold_recipe ? r1 : r2
|
||||||
|
var/datum/chemical_reaction/warm_one = r1.is_cold_recipe ? r2 : r1
|
||||||
|
if(warm_one.required_temp == 0 || cold_one.required_temp < warm_one.required_temp)
|
||||||
|
// the warm reaction doesn't require any particular temperature or the range of temperatures does not overlap, so there is no conflict
|
||||||
|
return FALSE
|
||||||
|
|
||||||
|
//find the reactions with the shorter and longer required_reagents list
|
||||||
|
var/datum/chemical_reaction/long_req
|
||||||
|
var/datum/chemical_reaction/short_req
|
||||||
|
if(r1.required_reagents.len > r2.required_reagents.len)
|
||||||
|
long_req = r1
|
||||||
|
short_req = r2
|
||||||
|
else if(r1.required_reagents.len < r2.required_reagents.len)
|
||||||
|
long_req = r2
|
||||||
|
short_req = r1
|
||||||
|
else
|
||||||
|
//if they are the same length, sort instead by the length of the catalyst list
|
||||||
|
//this is important if the required_reagents lists are the same
|
||||||
|
if(r1.required_catalysts.len > r2.required_catalysts.len)
|
||||||
|
long_req = r1
|
||||||
|
short_req = r2
|
||||||
|
else
|
||||||
|
long_req = r2
|
||||||
|
short_req = r1
|
||||||
|
|
||||||
|
|
||||||
|
//check if the shorter reaction list is a subset of the longer one
|
||||||
|
var/list/overlap = r1.required_reagents & r2.required_reagents
|
||||||
|
if(overlap.len != short_req.required_reagents.len)
|
||||||
|
//there is at least one reagent in the short list that is not in the long list, so there is no conflict
|
||||||
|
return FALSE
|
||||||
|
|
||||||
|
//check to see if the shorter reaction's catalyst list is also a subset of the longer reaction's catalyst list
|
||||||
|
//if the longer reaction's catalyst list is a subset of the shorter ones, that is fine
|
||||||
|
//if the reaction lists are the same, the short reaction will have the shorter required_catalysts list, so it will register as a conflict
|
||||||
|
var/list/short_minus_long_catalysts = short_req.required_catalysts - long_req.required_catalysts
|
||||||
|
if(short_minus_long_catalysts.len)
|
||||||
|
//there is at least one unique catalyst for the short reaction, so there is no conflict
|
||||||
|
return FALSE
|
||||||
|
|
||||||
|
//if we got this far, the longer reaction will be impossible to create if the shorter one is earlier in global.chemical_reactions_list, and will require the reagents to be added in a particular order otherwise
|
||||||
|
return TRUE
|
||||||
72
code/modules/unit_tests/unit_test.dm
Normal file
72
code/modules/unit_tests/unit_test.dm
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
Usage:
|
||||||
|
Override /run() to run your test code.
|
||||||
|
Call fail() to fail the test (You should specify a reason).
|
||||||
|
Use /New() and Destroy() for setup/teardown, respectively.
|
||||||
|
You can use the run_loc_bottom_left and run_loc_top_right if your tests require turfs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var/datum/unit_test/current_test
|
||||||
|
var/failed_any_test = FALSE
|
||||||
|
|
||||||
|
/datum/unit_test
|
||||||
|
var/list/procs_tested
|
||||||
|
|
||||||
|
//usable vars
|
||||||
|
var/turf/run_loc_bottom_left
|
||||||
|
var/turf/run_loc_top_right
|
||||||
|
|
||||||
|
//internal vars
|
||||||
|
var/succeeded = TRUE
|
||||||
|
var/list/fail_reasons
|
||||||
|
|
||||||
|
/datum/unit_test/New()
|
||||||
|
run_loc_bottom_left = locate(1, 1, 1)
|
||||||
|
run_loc_top_right = locate(5, 5, 1)
|
||||||
|
|
||||||
|
/datum/unit_test/Destroy()
|
||||||
|
//clear the test area
|
||||||
|
for(var/atom/movable/AM in block(run_loc_bottom_left, run_loc_top_right))
|
||||||
|
qdel(AM)
|
||||||
|
..()
|
||||||
|
|
||||||
|
/datum/unit_test/proc/start()
|
||||||
|
fail("run() called parent or not implemented")
|
||||||
|
|
||||||
|
/datum/unit_test/proc/fail(var/reason = "No reason provided")
|
||||||
|
succeeded = FALSE
|
||||||
|
|
||||||
|
if(!istext(reason))
|
||||||
|
reason = "FORMATTED: [isnull(reason) ? "NULL" : "reason"]"
|
||||||
|
|
||||||
|
if(!fail_reasons)
|
||||||
|
fail_reasons = list()
|
||||||
|
fail_reasons.Add(reason)
|
||||||
|
|
||||||
|
/proc/run_unit_tests()
|
||||||
|
CHECK_TICK
|
||||||
|
|
||||||
|
for(var/I in subtypesof(/datum/unit_test))
|
||||||
|
var/datum/unit_test/test = new I
|
||||||
|
|
||||||
|
global.current_test = test
|
||||||
|
var/duration = world.timeofday
|
||||||
|
|
||||||
|
test.start()
|
||||||
|
|
||||||
|
duration = world.timeofday - duration
|
||||||
|
global.current_test = null
|
||||||
|
global.failed_any_test |= !test.succeeded
|
||||||
|
|
||||||
|
var/list/log_entry = list("UNIT TEST [test.succeeded ? "PASS" : "FAIL"]: [I] [duration / 10]s")
|
||||||
|
var/list/fail_reasons = test.fail_reasons
|
||||||
|
|
||||||
|
qdel(test)
|
||||||
|
|
||||||
|
for(var/J in 1 to length(fail_reasons))
|
||||||
|
log_entry.Add("\tREASON #[J]: [fail_reasons[J]]")
|
||||||
|
world.log << log_entry.Join("\n")
|
||||||
|
|
||||||
|
CHECK_TICK
|
||||||
|
|
||||||
|
del(world)
|
||||||
@@ -140,13 +140,12 @@ var/savefile/panicfile
|
|||||||
|
|
||||||
src.update_status()
|
src.update_status()
|
||||||
|
|
||||||
sleep_offline = 1
|
sleep_offline = 0
|
||||||
|
|
||||||
send2mainirc("Server starting up on [config.server? "byond://[config.server]" : "byond://[world.address]:[world.port]"]")
|
send2mainirc("Server starting up on [config.server? "byond://[config.server]" : "byond://[world.address]:[world.port]"]")
|
||||||
send2maindiscord("**Server starting up** on `[config.server? "byond://[config.server]" : "byond://[world.address]:[world.port]"]`. Map is **[map.nameLong]**")
|
send2maindiscord("**Server starting up** on `[config.server? "byond://[config.server]" : "byond://[world.address]:[world.port]"]`. Map is **[map.nameLong]**")
|
||||||
|
|
||||||
spawn(10)
|
Master.Setup()
|
||||||
Master.Setup()
|
|
||||||
|
|
||||||
process_teleport_locs() //Sets up the wizard teleport locations
|
process_teleport_locs() //Sets up the wizard teleport locations
|
||||||
process_ghost_teleport_locs() //Sets up ghost teleport locations.
|
process_ghost_teleport_locs() //Sets up ghost teleport locations.
|
||||||
@@ -160,7 +159,6 @@ var/savefile/panicfile
|
|||||||
KickInactiveClients()*/
|
KickInactiveClients()*/
|
||||||
|
|
||||||
#undef RECOMMENDED_VERSION
|
#undef RECOMMENDED_VERSION
|
||||||
|
|
||||||
return ..()
|
return ..()
|
||||||
|
|
||||||
//world/Topic(href, href_list[])
|
//world/Topic(href, href_list[])
|
||||||
|
|||||||
@@ -1,43 +1,41 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import argparse
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import distutils.spawn
|
import distutils.spawn
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
import travis_utils
|
||||||
|
|
||||||
MAP_INCLUDE_RE = re.compile(r"#include \"maps\\[a-zA-Z0-9][a-zA-Z0-9_]*\.dm\"")
|
MAP_INCLUDE_RE = re.compile(r"#include \"maps\\[a-zA-Z0-9][a-zA-Z0-9_]*\.dm\"")
|
||||||
|
|
||||||
ensure_future = None
|
|
||||||
|
|
||||||
# ensure_future is new in 3.4.4, previously it was asyncio.async.
|
|
||||||
try:
|
|
||||||
ensure_future = asyncio.ensure_future
|
|
||||||
except AttributeError:
|
|
||||||
# Can't directly do asyncio.async because async is a keyword now,
|
|
||||||
# and that'd parse error on newer versions.
|
|
||||||
ensure_future = getattr(asyncio, "async")
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser()
|
dme = os.environ.get("PROJECT_NAME") # The DME file to compile.
|
||||||
parser.add_argument("dme", help="The DME file to compile.")
|
if not dme:
|
||||||
parser.add_argument("-M", "--mapfile", nargs="*", help="Extra map files to replace the regular map file in the DME with.")
|
print("No project name specified.")
|
||||||
args = parser.parse_args()
|
exit(1)
|
||||||
|
dme += ".dme"
|
||||||
|
mapfiles = os.environ.get("ALL_MAPS") # Extra map files to replace the regular map file in the DME with.
|
||||||
|
build_tests = os.environ.get("DM_UNIT_TESTS") == "1" # Whether to build unit tests or not.
|
||||||
|
|
||||||
dme = args.dme
|
if build_tests is True and mapfiles is not None:
|
||||||
if args.mapfile is not None:
|
print("Cannot run tests AND change maps at the same time, overriding ALL_MAPS.") # Because BYOND will cry "corrupt map data in world file"
|
||||||
# Handle map file replacement.
|
mapfiles = "test_tiny"
|
||||||
with open(dme, "r") as f:
|
|
||||||
|
if build_tests is True:
|
||||||
|
with open(dme, "r+") as f:
|
||||||
content = f.read()
|
content = f.read()
|
||||||
|
f.seek(0, 0)
|
||||||
|
f.write("#define UNIT_TESTS\n" + content)
|
||||||
|
|
||||||
# Make string to replace the map include with.
|
if mapfiles is not None:
|
||||||
includes = ""
|
with open(dme, "r+") as f:
|
||||||
for arg in args.mapfile:
|
content = f.read()
|
||||||
includes += "#include \"maps\\\\{}.dm\"\n".format(arg)
|
includes = ""
|
||||||
|
for arg in mapfiles.split():
|
||||||
content = MAP_INCLUDE_RE.sub(includes, content, count=1)
|
includes += "#include \"maps\\\\{}.dm\"\n".format(arg)
|
||||||
dme = "{}.mdme".format(dme)
|
content = MAP_INCLUDE_RE.sub(includes, content, count=1)
|
||||||
with open(dme, "w") as f:
|
f.seek(0, 0)
|
||||||
f.write(content)
|
f.write(content)
|
||||||
|
|
||||||
compiler = "DreamMaker"
|
compiler = "DreamMaker"
|
||||||
@@ -49,28 +47,10 @@ def main():
|
|||||||
print("Unable to find DM compiler.")
|
print("Unable to find DM compiler.")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
loop = asyncio.get_event_loop()
|
loop = travis_utils.get_platform_event_loop()
|
||||||
code = loop.run_until_complete(run_compiler([compiler, dme]))
|
code = loop.run_until_complete(travis_utils.run_with_timeout_guards([compiler, dme]))
|
||||||
exit(code)
|
exit(code)
|
||||||
|
|
||||||
# DM SOMEHOW manages to go 10 minutes without logging anything nowadays.
|
|
||||||
# So... Travis kills it.
|
|
||||||
# Thanks DM.
|
|
||||||
# This repeats messages like travis_wait (which I couldn't get working) does to prevent that.
|
|
||||||
@asyncio.coroutine
|
|
||||||
def run_compiler(args):
|
|
||||||
compiler_process = yield from asyncio.create_subprocess_exec(*args)
|
|
||||||
task = ensure_future(print_timeout_guards())
|
|
||||||
|
|
||||||
ret = yield from compiler_process.wait()
|
|
||||||
task.cancel()
|
|
||||||
return ret
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
|
||||||
def print_timeout_guards():
|
|
||||||
while True:
|
|
||||||
yield from asyncio.sleep(8*60)
|
|
||||||
print("Keeping Travis alive. Ignore this!")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
17
tools/travis/config/config.txt
Normal file
17
tools/travis/config/config.txt
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
ADMIN_LEGACY_SYSTEM
|
||||||
|
BAN_LEGACY_SYSTEM
|
||||||
|
|
||||||
|
PROBABILITY EXTENDED 5
|
||||||
|
|
||||||
|
ALLOW_HOLIDAYS
|
||||||
|
|
||||||
|
TICKLAG 0.33
|
||||||
|
|
||||||
|
TICKCOMP 0
|
||||||
|
|
||||||
|
SKIP_MINIMAP_GENERATION
|
||||||
|
SKIP_VAULT_GENERATION
|
||||||
|
|
||||||
|
SHUT_UP_AUTOMATIC_DIAGNOSTIC_AND_ANNOUNCEMENT_SYSTEM
|
||||||
|
|
||||||
|
ENABLE_WAGES
|
||||||
30
tools/travis/run_tests.py
Executable file
30
tools/travis/run_tests.py
Executable file
@@ -0,0 +1,30 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import distutils.spawn
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
import travis_utils
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if os.environ.get("DM_UNIT_TESTS") != "1":
|
||||||
|
print("DM_UNIT_TESTS is not set, not running tests.")
|
||||||
|
return
|
||||||
|
|
||||||
|
dmb = os.environ.get("PROJECT_NAME") # The DMB file to run.
|
||||||
|
if not dmb:
|
||||||
|
print("No project name specified.")
|
||||||
|
exit(1)
|
||||||
|
dmb += ".dmb"
|
||||||
|
|
||||||
|
executable = "DreamDaemon"
|
||||||
|
dreamdaemon = distutils.spawn.find_executable(executable)
|
||||||
|
if not dreamdaemon:
|
||||||
|
print("Unable to find {}.".format(executable))
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
loop = travis_utils.get_platform_event_loop()
|
||||||
|
code = loop.run_until_complete(travis_utils.run_with_timeout_guards([dreamdaemon, dmb, "-close", "-trusted"]))
|
||||||
|
exit(code)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
40
tools/travis/travis_utils.py
Normal file
40
tools/travis/travis_utils.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import asyncio
|
||||||
|
import sys
|
||||||
|
|
||||||
|
ensure_future = None
|
||||||
|
|
||||||
|
# ensure_future is new in 3.4.4, previously it was asyncio.async.
|
||||||
|
try:
|
||||||
|
ensure_future = asyncio.ensure_future
|
||||||
|
except AttributeError:
|
||||||
|
# Can't directly do asyncio.async because async is a keyword now,
|
||||||
|
# and that'd parse error on newer versions.
|
||||||
|
ensure_future = getattr(asyncio, "async")
|
||||||
|
|
||||||
|
# DM SOMEHOW manages to go 10 minutes without logging anything nowadays.
|
||||||
|
# So... Travis kills it.
|
||||||
|
# Thanks DM.
|
||||||
|
# This repeats messages like travis_wait (which I couldn't get working) does to prevent that.
|
||||||
|
@asyncio.coroutine
|
||||||
|
def run_with_timeout_guards(args):
|
||||||
|
target_process = yield from asyncio.create_subprocess_exec(*args, stderr=asyncio.subprocess.STDOUT)
|
||||||
|
task = ensure_future(print_timeout_guards())
|
||||||
|
|
||||||
|
ret = yield from target_process.wait()
|
||||||
|
task.cancel()
|
||||||
|
return ret
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def print_timeout_guards():
|
||||||
|
while True:
|
||||||
|
yield from asyncio.sleep(8*60)
|
||||||
|
print("Keeping Travis alive. Ignore this!")
|
||||||
|
|
||||||
|
# Windows needs a different event loop to manage subprocesses
|
||||||
|
def get_platform_event_loop():
|
||||||
|
if sys.platform == "win32" or sys.platform == "cygwin":
|
||||||
|
loop = asyncio.ProactorEventLoop()
|
||||||
|
asyncio.set_event_loop(loop)
|
||||||
|
return loop
|
||||||
|
else:
|
||||||
|
return asyncio.get_event_loop()
|
||||||
@@ -2221,6 +2221,7 @@
|
|||||||
#include "code\modules\telesci\telepad.dm"
|
#include "code\modules\telesci\telepad.dm"
|
||||||
#include "code\modules\telesci\telesci_computer.dm"
|
#include "code\modules\telesci\telesci_computer.dm"
|
||||||
#include "code\modules\tooltip\tooltip.dm"
|
#include "code\modules\tooltip\tooltip.dm"
|
||||||
|
#include "code\modules\unit_tests\_unit_tests.dm"
|
||||||
#include "code\modules\virus2\analyser.dm"
|
#include "code\modules\virus2\analyser.dm"
|
||||||
#include "code\modules\virus2\antibodies.dm"
|
#include "code\modules\virus2\antibodies.dm"
|
||||||
#include "code\modules\virus2\centrifuge.dm"
|
#include "code\modules\virus2\centrifuge.dm"
|
||||||
|
|||||||
Reference in New Issue
Block a user