Skip to content
This repository has been archived by the owner on May 13, 2020. It is now read-only.

Add integration tests for 'stable env' #73

Merged
merged 8 commits into from
Jul 10, 2018

Conversation

srenatus
Copy link
Contributor

Turns out this is probably the lowest-hanging fruit right now. This makes
use of the previous commit's "run in temp dir" feature.

Part of #28.

Happy to discuss the approach taken in this PR's first commit -- using the shell wrapper to cd into the right place. I've tried getting this to work in some other way -- using an exclusion group for side-stepping concurrency issues in @chdir, but couldn't get it to work. After that, the present solution seemed OK enough 😉

Curious about all the input I can get here :) What I dislike most is the duplication in the new env tests, but I couldn't come up with a better structure.

Having not found a way to make `@chdir[I32]` or anything like that work,
this commit adds a bash helper script that `cd`s into the directory
before running stable.

Also includes the code changes necessary to create the temporary dir
from pony, and remove it after the tests are done.

The former is done so that upcoming tests can use pony to set up some
initial situation (such as drop a bundle.json).

The cleanup is not done in a `tear_down()` method, because I found no
non-clumsy way to get the reference to the directory into that spot. So
instead, this has a mini-actor that deletes the temp dir when disposed,
and is set up to be called by the test helper.

Note that there's no strict need to run the existing integration tests
in their own temp dirs, so we might make that temp dir passing optional,
maybe, by changing `Exec`'s `cleaner arg from

    cleaner: DisposableActor

to

    cleaner: (DisposableActor | None)

and pass None for help and version.

Signed-off-by: Stephan Renatus <[email protected]>
Turns out this is probably the lowest-hanging fruit right now. This makes
use of the previous commit's "run in temp dir" feature.

Signed-off-by: Stephan Renatus <[email protected]>
@srenatus srenatus mentioned this pull request Jun 18, 2018
…ent)

This is in anticipation of what's going to change in ponylang#68.

Signed-off-by: Stephan Renatus <[email protected]>
h.long_test(100_000_000)
let tmp =
try
FilePath.mkdtemp(h.env.root as AmbientAuth, "")?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❔ The "" is a bit icky, as it'll create the temp dir in the current working directory -- i.e., the checkout of pony-stable. It'll get cleaned up, but it still strikes me as a questionable default. (OTOH, I'm not sure if /tmp is much better, but I might be overthinking this. We could also create an empty tmp dir in the repo, and use that...)

@srenatus
Copy link
Contributor Author

@mfelsche @jemc if anyone of you got a moment, I'd appreciate a glance. 😃

@mfelsche
Copy link
Contributor

I am fine with creating a temporary directory in the current folder in general as long as it is guaranteed to not clash with anything existing.

I wont have much time in the next months but ill try to have a look at this soonish.

Thanks for your efforts in bringing stable forward.

This adds stable/test/integration/tmp/ as an empty, tracked folder; and
uses that one as base directory for running the integration tests.

It felt a bit cleaner to me than having the temp dirs created in the
top-level folder; also causes less of a mess when debugging integration
tests by disabling the cleanup dispose()'s delete call.

(I suppose it would have been just as good to use /tmp/, but I wasn't
sure what our expectations regarding different operating systems were.)

Signed-off-by: Stephan Renatus <[email protected]>
@srenatus
Copy link
Contributor Author

🤔 I'm a little concerned that this has failed on CircleCI for some reason -- this indicates that the tests are flakey, which will be annoying long-term.

However, I can run make integration in a loop without any failures. Just tried a few hundred iterations. This makes me suspect that it could be an actual timeout, i.e. the execution on the (presumably shared) infrastructure provided by circleci may just be too slow every now and then. This hypothesis is difficult to verify, but I suppose doubling timeouts for integration tests is a thing we could try (100ms -> 200ms).

@srenatus
Copy link
Contributor Author

srenatus commented Jun 26, 2018

Huh, this happened again right away. Trying to dig in...

Running the tests locally using the circleci tool:

$ make clean # will pick up wrong arch binary otherwise (not rebuild it for linux)
rm -rf build/release
$ circleci build --job vs-ponyc-release

====>> Spin up Environment
Build-agent version 0.0.5895-01cdb92 (2018-05-29T20:07:10+0000)
Starting container ponylang/ponyc:release
  using image ponylang/ponyc@sha256:765b1184c04f69d46cd7174790634b5badfbc3e6345459337c1dba0f9f3a3872

Using build environment variables:
  BASH_ENV=/tmp/.bash_env-localbuild-1529996007
  CI=true
  CIRCLECI=true
  CIRCLE_BRANCH=sr/integration-test/env
  CIRCLE_BUILD_NUM=
  CIRCLE_JOB=vs-ponyc-release
  CIRCLE_NODE_INDEX=0
  CIRCLE_NODE_TOTAL=1
  CIRCLE_REPOSITORY_URL=https://github.com/ponylang/pony-stable
  CIRCLE_SHA1=8d8a906c69921050b18eded39159c1073afb071d
  CIRCLE_SHELL_ENV=/tmp/.bash_env-localbuild-1529996007
  CIRCLE_WORKING_DIRECTORY=~/project

====>> Checkout code
  #!/bin/sh
mkdir -p /root/project && cp -r /tmp/_circleci_local_build_repo/. /root/project
====>> make test integration
  #!/bin/bash -eo pipefail
make test integration
mkdir -p build/release
ponyc  --debug -o build/release stable/test
Building builtin -> /usr/local/lib/pony/0.23.0/packages/builtin
Building stable/test -> /root/project/stable/test
Building ponytest -> /usr/local/lib/pony/0.23.0/packages/ponytest
Building time -> /usr/local/lib/pony/0.23.0/packages/time
Building collections -> /usr/local/lib/pony/0.23.0/packages/collections
Building files -> /usr/local/lib/pony/0.23.0/packages/files
Building buffered -> /usr/local/lib/pony/0.23.0/packages/buffered
Building term -> /usr/local/lib/pony/0.23.0/packages/term
Building promises -> /usr/local/lib/pony/0.23.0/packages/promises
Building strings -> /usr/local/lib/pony/0.23.0/packages/strings
Building signals -> /usr/local/lib/pony/0.23.0/packages/signals
Building capsicum -> /usr/local/lib/pony/0.23.0/packages/capsicum
Building .. -> /root/project/stable
Building json -> /usr/local/lib/pony/0.23.0/packages/json
Building format -> /usr/local/lib/pony/0.23.0/packages/format
Building options -> /usr/local/lib/pony/0.23.0/packages/options
Building integration -> /root/project/stable/test/integration
Building process -> /usr/local/lib/pony/0.23.0/packages/process
Building backpressure -> /usr/local/lib/pony/0.23.0/packages/backpressure
Building regex -> /usr/local/lib/pony/0.23.0/packages/regex
Generating
 Reachability
 Selector painting
 Data prototypes
 Data types
 Function prototypes
 Functions
 Descriptors
Writing build/release/test.o
Linking build/release/test
build/release/test --exclude=integration
1 test started, 0 complete: stable.Bundle started
1 test started, 1 complete: stable.Bundle complete
---- Passed: stable.Bundle
----
---- 1 test ran.
---- Passed: 1
ponyc  stable -o build/release
Building builtin -> /usr/local/lib/pony/0.23.0/packages/builtin
Building stable -> /root/project/stable
Building files -> /usr/local/lib/pony/0.23.0/packages/files
Building time -> /usr/local/lib/pony/0.23.0/packages/time
Building collections -> /usr/local/lib/pony/0.23.0/packages/collections
Building ponytest -> /usr/local/lib/pony/0.23.0/packages/ponytest
Building buffered -> /usr/local/lib/pony/0.23.0/packages/buffered
Building term -> /usr/local/lib/pony/0.23.0/packages/term
Building promises -> /usr/local/lib/pony/0.23.0/packages/promises
Building strings -> /usr/local/lib/pony/0.23.0/packages/strings
Building signals -> /usr/local/lib/pony/0.23.0/packages/signals
Building capsicum -> /usr/local/lib/pony/0.23.0/packages/capsicum
Building json -> /usr/local/lib/pony/0.23.0/packages/json
Building format -> /usr/local/lib/pony/0.23.0/packages/format
Building options -> /usr/local/lib/pony/0.23.0/packages/options
Generating
 Reachability
 Selector painting
 Data prototypes
 Data types
 Function prototypes
 Functions
 Descriptors
Optimising
Writing build/release/stable.o
Linking build/release/stable
STABLE_BIN=$(pwd)/build/release/stable build/release/test --only=integration
1 test started, 0 complete: integration.Usage() started
2 tests started, 0 complete: integration.Usage(help) started
3 tests started, 0 complete: integration.Usage(unknown arguments) started
4 tests started, 0 complete: integration.Version started
5 tests started, 0 complete: integration.Env(no bundle.json) started
6 tests started, 0 complete: integration.Env(empty bundle in same directory) started
7 tests started, 0 complete: integration.Env(bundle in same directory) started
8 tests started, 0 complete: integration.Env(calling a program) started
9 tests started, 0 complete: integration.Env(bundle.json in parent dir) started
10 tests started, 0 complete: integration.Env(invalid bundle.json in nested dir) started
10 tests started, 1 complete: integration.Version complete
10 tests started, 2 complete: integration.Usage(help) complete
10 tests started, 3 complete: integration.Usage(unknown arguments) complete
10 tests started, 4 complete: integration.Env(empty bundle in same directory) complete
10 tests started, 5 complete: integration.Env(bundle.json in parent dir) complete
10 tests started, 6 complete: integration.Usage() complete
10 tests started, 7 complete: integration.Env(invalid bundle.json in nested dir) complete
10 tests started, 8 complete: integration.Env(calling a program) complete
10 tests started, 9 complete: integration.Env(bundle in same directory) complete
10 tests started, 10 complete: integration.Env(no bundle.json) complete
---- Passed: integration.Usage()
---- Passed: integration.Usage(help)
---- Passed: integration.Usage(unknown arguments)
---- Passed: integration.Version
---- Passed: integration.Env(no bundle.json)
---- Passed: integration.Env(empty bundle in same directory)
---- Passed: integration.Env(bundle in same directory)
---- Passed: integration.Env(calling a program)
---- Passed: integration.Env(bundle.json in parent dir)
---- Passed: integration.Env(invalid bundle.json in nested dir)
----
---- 10 tests ran.
---- Passed: 10
Success!
$

Running this in a loop for a while; let's see if the failure comes up locally...

Update: ran this 25 times with no issue. (It takes a lot longer than a make integration loop; so the n is smaller here; didn't wait that long to try this forever.)

Next up: limiting the docker resources locally to the CircleCI specs -- 4G mem, 2 cpu -- and running docker run -v $(pwd):/src/main ponylang/ponyc:release make integration in a loop.

Update: no issues after having run that ^ 100 times.

Trying to restrict resources further, I've found that giving the job the minimum memory possible in docker, 4M, reproducibly gives timeouts:

(Note that this only works if you've run docker run -v $(pwd):/src/main ponylang/ponyc:release make integration before -- you cannot build the test binary with 4M mem)

$ docker run -m 4m -v $(pwd):/src/main ponylang/ponyc:release make integration
STABLE_BIN=$(pwd)/build/release/stable build/release/test --only=integration
1 test started, 0 complete: integration.Usage() started
2 tests started, 0 complete: integration.Usage(help) started
3 tests started, 0 complete: integration.Usage(unknown arguments) started
4 tests started, 0 complete: integration.Version started
5 tests started, 0 complete: integration.Env(no bundle.json) started
6 tests started, 0 complete: integration.Env(empty bundle in same directory) started
7 tests started, 0 complete: integration.Env(bundle in same directory) started
8 tests started, 0 complete: integration.Env(calling a program) started
9 tests started, 0 complete: integration.Env(bundle.json in parent dir) started
10 tests started, 0 complete: integration.Env(invalid bundle.json in nested dir) started
10 tests started, 1 complete: integration.Usage(help) complete
10 tests started, 2 complete: integration.Usage() complete
10 tests started, 3 complete: integration.Version complete
10 tests started, 4 complete: integration.Env(bundle in same directory) complete
10 tests started, 5 complete: integration.Env(empty bundle in same directory) complete
10 tests started, 6 complete: integration.Usage(unknown arguments) complete
10 tests started, 7 complete: integration.Env(no bundle.json) complete
10 tests started, 8 complete: integration.Env(calling a program) complete
10 tests started, 9 complete: integration.Env(invalid bundle.json in nested dir) complete
10 tests started, 10 complete: integration.Env(bundle.json in parent dir) complete
---- Passed: integration.Usage()
---- Passed: integration.Usage(help)
---- Passed: integration.Usage(unknown arguments)
**** FAILED: integration.Version
Test timed out without completing
stdout was: 0.1.3-353733f [release]

---- Passed: integration.Env(no bundle.json)
**** FAILED: integration.Env(empty bundle in same directory)
Test timed out without completing
**** FAILED: integration.Env(bundle in same directory)
Test timed out without completing
stdout was: SHLVL=0
OLDPWD=/src/main
CWD=/src/main/stable/test/integration/tmp/P7YjXX
STABLE_BIN=/src/main/build/release/stable
PWD=/src/main/stable/test/integration/tmp/P7YjXX
PONYPATH=../local/a:/src/main/stable/test/integration/tmp/P7YjXX/.deps/-local-git-b16798852821555717647:/src/main/stable/test/integration/tmp/P7YjXX/.deps/github/c:/src/main/stable/test/integration/tmp/P7YjXX/.deps/gitlab/d

---- Passed: integration.Env(calling a program)
---- Passed: integration.Env(bundle.json in parent dir)
**** FAILED: integration.Env(invalid bundle.json in nested dir)
Test timed out without completing
----
---- 10 tests ran.
---- Passed: 6
**** FAILED: 4 tests, listed below:
**** FAILED: integration.Version
**** FAILED: integration.Env(empty bundle in same directory)
**** FAILED: integration.Env(bundle in same directory)
**** FAILED: integration.Env(invalid bundle.json in nested dir)
Makefile:53: recipe for target 'integration' failed
make: *** [integration] Error 255
$

However, bumping the timeouts to 1s even makes these go away when running in 4M mem containers.

I would thus think that this could be a compromise. The hypothesis is that a timeout of 1s is enough; and the next commit, bumping timeouts further, shall make this PR pass on circleci.

@SeanTAllen
Copy link
Member

@srenatus i would suggest running the tests using the --sequential flag. That will run them one at a time and should fix your timeout problem. Then they aren't competing for resources, you can also put them in an exclusion group together so no more than 1 member of an exclusion group runs at a time. --sequential (like we do for ponyc tests) is probably your best option.

@srenatus
Copy link
Contributor Author

srenatus commented Jun 26, 2018

@SeanTAllen good idea! Thanks 🎉

Update: note those two commits below -- seems like a modest timeout bump (200ms) and running sequentially makes this OK.

In any case, it's something to keep an eye on. If this becomes too annoying (it shouldn't impede contributions), I'm happy to revisit it.

One of these tests had timed out on circleci. Having flakey tests seems
an annoyance we should try to avoid; but I couldn't reproduce this by
running `make integration` in a loop locally. So, the hypothesis is that
the timeout was no bug in the tests, but a genuine timeout.

Trying to reproduce the test failures locally, I've found that
restricting the memory to 4M (docker's minimum) caused the tests to fail
in a reproducible way.

The integration test binary is now passed `--sequential`, so that the
individual test runs don't compete for resources.

This makes the error _not appear anymore_ in the docker 4M scenario.

This reverts commit 8d8a906.
Seems like --sequential alone isn't enough. 200ms doesn't seem too bad,
so let's try this.

Signed-off-by: Stephan Renatus <[email protected]>
Copy link
Contributor

@mfelsche mfelsche left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@@ -0,0 +1,3 @@
#!/bin/bash
cd "$CWD" || exit 100
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks like a hack. While this is fine, this shows that ProessMonitor should support setting the cwd like e.g. the python subprocess.Popen allows. I will create an issue in the ponyc repo for this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created ponylang/ponyc#2821 maybe link this somewhere in your code, so we track it and be able to remove this hack once ProcessMonitor supports setting the cwd.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! :)

let pm: ProcessMonitor = ProcessMonitor(auth, auth, consume notifier,
path, consume args, consume vars)
pm.done_writing()
h.dispose_when_done(pm)
h.dispose_when_done(cleaner)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think the test creating the tmpdir should be responsible for cleaning it up. So just call this right after the temporary directory has been created.

@srenatus
Copy link
Contributor Author

srenatus commented Jul 10, 2018

Hiccups on travis, I've tried re-running. If it doesn't do the trick, I can deal with this later. (Tomorrow morning the latest.)

@srenatus
Copy link
Contributor Author

Ok the travis issue has resolved itself -- added a note re: the ponyc issue, will merge this when green.

@srenatus srenatus merged commit 447a013 into ponylang:master Jul 10, 2018
@srenatus srenatus deleted the sr/integration-test/env branch July 10, 2018 15:11
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants