1

I'm dealing with a weird problem where a few of my tests are dying from exceptions when run inside of SimpleTest, but I'm unable to reproduce the problem outside of the SimpleTest environment. In particular, it appears that exceptions that I'm catching in my module code get intercepted by SimpleTest before they even leave my try block.

I'm able to debug my test script using xdebug, but for whatever reason, I can't get SimpleTest to pass on the debugger info to my code when it's running under SimpleTest. So when SimpleTest calls $test->run() on one of my tests, the test starts up in its own, SimpleTest-unaware process. And my debugger sits on its hands (they have hands, right?) and runs as if everything is copacetic.

Is there some option in SimpleTest that will allow me to get the XDEBUG_CONFIG environment variable passed on to the subprocesses that SimpleTest spawns?

  • It looks like tests are dispatched via the batch/queue mechanism in Drupal; when SimpleTest sets up its batch run, it's calling "putenv('SIMPLETEST_BASE_URL=' . $base_url)", which I'm guessing is where I need to add my XDEBUG_CONFIG magic. Is there hopefully a cleaner way to do this? Like manipulating the URL that handles the batch item? – Torenware Networks Oct 19 '15 at 2:05
  • What kind of exceptions? Batch is JS/browser requests, that should definitely still work. A common problem is the amount of parallel requests, PhpStorm for example has a setting that by defaults limits the amount of concurrent debugging sessions to 1. Set that to 5 or so, search for xdebug or so in the settings. One thing that is currently broken is passing the XDEBUG config down to drupalGet() requests. – Berdir Oct 19 '15 at 7:14
  • No, it doesn't matter if you use JS/browser or run-test.sh. The problem is that your program is called via drupalGet() and similar, I've just figured out. So unless you pass XDEBUG_SESSION_START as a get parameter to drupalGet() (or drupalPostForm, etc.), your program will be unaware of XDEBUG. So a hack solution is to pass the $options parameter to that. Which I'll write up as an initial answer. If you can figure out a better one, I'd love to hear it. – Torenware Networks Oct 19 '15 at 7:36
  • @Berdir: As for the exception I'm trying to track down: I was calling file_delete() inside a try block which works fine running the app, but not under SimpleTest. Turns out that it's a cache problem: when running under SimpleTest. You die in entity_delete_multiple() when the storage controller cannot find your file objects. What's worse is that the exception handler (probably installed by SimpleTest) call drupal's shutdown code before the exception is even allowed to propagate. Which sucks, royally. – Torenware Networks Oct 19 '15 at 8:11
0

I have a provisional answer to this, although I'm hoping someone will supply a better one than this one.

First of all, setting XDEBUG_CONFIG as an exported environment variable will let you stop your test script, but not your actual Drupal code. The reason is sort of ugly. SimpleTest interacts with your code using its own mini-browser. So your SimpleTest test runner is as completely isolated from your Drupal application as your browser at home is isolated from the web server you get your page from. There's no connection at all.

So how do we use xdebug via a browser? One way is to set a get parameter, and add the XDEBUG_SESSION_START variable to your URL, like this:

http://my-site-i-want-to-test/node/42?XDEBUG_SESSION_START=my-key

When your test web server sees XDEBUG_SESSION_START, and you have your server set up to connect with your IDE, your server connects up with your IDE, and away you go.

So, here's my not-really-very-good solution to the problem: I get drupalGet and its twins to add XDEBUG_SESSION_START to the string SimpleTest uses to communicate with your server.

SimpleTest is actually using PHP's curl extension to create its mini-browser. If you look at the implementation of drupalGet in WebTestBase.php, you'll see something like this:

  protected function drupalGet($path, array $options = array(), array $headers = array()) {
    // We re-using a CURL connection here. If that connection still has certain
    // options set, it might change the GET into a POST. Make sure we clear out
    // previous options.
    $out = $this->curlExec(array(CURLOPT_HTTPGET => TRUE, CURLOPT_URL => $this->buildUrl($path, $options), CURLOPT_NOBODY => FALSE, CURLOPT_HTTPHEADER => $headers));

    // lots of stuff omitted here...

    return $out;
  }

The $options argument gets passed to $this->buildUrl(), which takes the regular format for URL options. We need to add something to the query string, so we need to pass

$options = [
  'query' => [
    'XDEBUG_SESSION_START' => 'my-key',
  ],
];

This unfortunately requires you to edit your test and pass $options whenever you want to get control of your application via your debugger.

A slightly better solution is to override drupalGet() in your test class, since AFAICT, all the other drupal*() methods ultimately call drupalGet(). In your overrided version, you just append the 'query' info above to the $option array that is passed to it, and then call the original method. You can even watch for a environmental variable in your SimpleTest test script, and only add the 'query' string when you see it.

  • This is supposed to work out of the box, but as I wrote, it is currently broken: drupal.org/node/2552267 – Berdir Oct 19 '15 at 8:43
  • Correction: It is supposed to work if you use the cookie. THE GET argument is a completely different story. – Berdir Oct 19 '15 at 8:44
  • Your patch in #2552267 is kind of nice, and I'd guess the idea could be used in a WebTestBase subclass w/o hacking core. Although when the children are also under xdebug, it is god-awful slow :-). You really gotta need it. – Torenware Networks Oct 19 '15 at 16:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.