PageRenderTime 89ms CodeModel.GetById 2ms app.highlight 80ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/llcommon/tests/lleventcoro_test.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 799 lines | 627 code | 64 blank | 108 comment | 14 complexity | 6bc23e9cde450b1ca438857f4d18fbbe MD5 | raw file
  1/**
  2 * @file   coroutine_test.cpp
  3 * @author Nat Goodspeed
  4 * @date   2009-04-22
  5 * @brief  Test for coroutine.
  6 * 
  7 * $LicenseInfo:firstyear=2009&license=viewerlgpl$
  8 * Second Life Viewer Source Code
  9 * Copyright (C) 2010, Linden Research, Inc.
 10 * 
 11 * This library is free software; you can redistribute it and/or
 12 * modify it under the terms of the GNU Lesser General Public
 13 * License as published by the Free Software Foundation;
 14 * version 2.1 of the License only.
 15 * 
 16 * This library is distributed in the hope that it will be useful,
 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 19 * Lesser General Public License for more details.
 20 * 
 21 * You should have received a copy of the GNU Lesser General Public
 22 * License along with this library; if not, write to the Free Software
 23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 24 * 
 25 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 26 * $/LicenseInfo$
 27 */
 28
 29/*****************************************************************************/
 30//  test<1>() is cloned from a Boost.Coroutine example program whose copyright
 31//  info is reproduced here:
 32/*---------------------------------------------------------------------------*/
 33//  Copyright (c) 2006, Giovanni P. Deretta
 34//
 35//  This code may be used under either of the following two licences:
 36//
 37//  Permission is hereby granted, free of charge, to any person obtaining a copy 
 38//  of this software and associated documentation files (the "Software"), to deal 
 39//  in the Software without restriction, including without limitation the rights 
 40//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
 41//  copies of the Software, and to permit persons to whom the Software is 
 42//  furnished to do so, subject to the following conditions:
 43//
 44//  The above copyright notice and this permission notice shall be included in 
 45//  all copies or substantial portions of the Software.
 46//
 47//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 48//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 49//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
 50//  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 51//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 52//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
 53//  THE SOFTWARE. OF SUCH DAMAGE.
 54//
 55//  Or:
 56//
 57//  Distributed under the Boost Software License, Version 1.0.
 58//  (See accompanying file LICENSE_1_0.txt or copy at
 59//  http://www.boost.org/LICENSE_1_0.txt)
 60/*****************************************************************************/
 61
 62// On some platforms, Boost.Coroutine must #define magic symbols before
 63// #including platform-API headers. Naturally, that's ineffective unless the
 64// Boost.Coroutine #include is the *first* #include of the platform header.
 65// That means that client code must generally #include Boost.Coroutine headers
 66// before anything else.
 67#include <boost/coroutine/coroutine.hpp>
 68// Normally, lleventcoro.h obviates future.hpp. We only include this because
 69// we implement a "by hand" test of future functionality.
 70#include <boost/coroutine/future.hpp>
 71#include <boost/bind.hpp>
 72#include <boost/range.hpp>
 73
 74#include "linden_common.h"
 75
 76#include <iostream>
 77#include <string>
 78
 79#include "../test/lltut.h"
 80#include "llsd.h"
 81#include "llevents.h"
 82#include "tests/wrapllerrs.h"
 83#include "stringize.h"
 84#include "lleventcoro.h"
 85#include "../test/debug.h"
 86
 87/*****************************************************************************
 88*   from the banana.cpp example program borrowed for test<1>()
 89*****************************************************************************/
 90namespace coroutines = boost::coroutines;
 91using coroutines::coroutine;
 92
 93template<typename Iter>
 94bool match(Iter first, Iter last, std::string match) {
 95  std::string::iterator i = match.begin();
 96  i != match.end();
 97  for(; (first != last) && (i != match.end()); ++i) {
 98    if (*first != *i)
 99      return false;
100    ++first;
101  }
102  return i == match.end();
103}
104
105template<typename BidirectionalIterator> 
106BidirectionalIterator 
107match_substring(BidirectionalIterator begin, 
108		BidirectionalIterator end, 
109		std::string xmatch,
110		BOOST_DEDUCED_TYPENAME coroutine<BidirectionalIterator(void)>::self& self) { 
111  BidirectionalIterator begin_ = begin;
112  for(; begin != end; ++begin) 
113    if(match(begin, end, xmatch)) {
114      self.yield(begin);
115    }
116  return end;
117} 
118
119typedef coroutine<std::string::iterator(void)> match_coroutine_type;
120
121/*****************************************************************************
122*   Test helpers
123*****************************************************************************/
124// I suspect this will be typical of coroutines used in Linden software
125typedef boost::coroutines::coroutine<void()> coroutine_type;
126
127/// Simulate an event API whose response is immediate: sent on receipt of the
128/// initial request, rather than after some delay. This is the case that
129/// distinguishes postAndWait() from calling post(), then calling
130/// waitForEventOn().
131class ImmediateAPI
132{
133public:
134    ImmediateAPI():
135        mPump("immediate", true)
136    {
137        mPump.listen("API", boost::bind(&ImmediateAPI::operator(), this, _1));
138    }
139
140    LLEventPump& getPump() { return mPump; }
141
142    // Invoke this with an LLSD map containing:
143    // ["value"]: Integer value. We will reply with ["value"] + 1.
144    // ["reply"]: Name of LLEventPump on which to send success response.
145    // ["error"]: Name of LLEventPump on which to send error response.
146    // ["fail"]: Presence of this key selects ["error"], else ["success"] as
147    // the name of the pump on which to send the response.
148    bool operator()(const LLSD& event) const
149    {
150        LLSD::Integer value(event["value"]);
151        LLSD::String replyPumpName(event.has("fail")? "error" : "reply");
152        LLEventPumps::instance().obtain(event[replyPumpName]).post(value + 1);
153        return false;
154    }
155
156private:
157    LLEventStream mPump;
158};
159
160/*****************************************************************************
161*   TUT
162*****************************************************************************/
163namespace tut
164{
165    struct coroutine_data
166    {
167        // Define coroutine bodies as methods here so they can use ensure*()
168
169        void explicit_wait(coroutine_type::self& self)
170        {
171            BEGIN
172            {
173                // ... do whatever preliminary stuff must happen ...
174
175                // declare the future
176                boost::coroutines::future<LLSD> future(self);
177                // tell the future what to wait for
178                LLTempBoundListener connection(
179                    LLEventPumps::instance().obtain("source").listen("coro", voidlistener(boost::coroutines::make_callback(future))));
180                ensure("Not yet", ! future);
181                // attempting to dereference ("resolve") the future causes the calling
182                // coroutine to wait for it
183                debug("about to wait");
184                result = *future;
185                ensure("Got it", future);
186            }
187            END
188        }
189
190        void waitForEventOn1(coroutine_type::self& self)
191        {
192            BEGIN
193            {
194                result = waitForEventOn(self, "source");
195            }
196            END
197        }
198
199        void waitForEventOn2(coroutine_type::self& self)
200        {
201            BEGIN
202            {
203                LLEventWithID pair = waitForEventOn(self, "reply", "error");
204                result = pair.first;
205                which  = pair.second;
206                debug(STRINGIZE("result = " << result << ", which = " << which));
207            }
208            END
209        }
210
211        void postAndWait1(coroutine_type::self& self)
212        {
213            BEGIN
214            {
215                result = postAndWait(self,
216                                     LLSD().insert("value", 17), // request event
217                                     immediateAPI.getPump(),     // requestPump
218                                     "reply1",                   // replyPump
219                                     "reply");                   // request["reply"] = name
220            }
221            END
222        }
223
224        void postAndWait2(coroutine_type::self& self)
225        {
226            BEGIN
227            {
228                LLEventWithID pair = ::postAndWait2(self,
229                                                    LLSD().insert("value", 18),
230                                                    immediateAPI.getPump(),
231                                                    "reply2",
232                                                    "error2",
233                                                    "reply",
234                                                    "error");
235                result = pair.first;
236                which  = pair.second;
237                debug(STRINGIZE("result = " << result << ", which = " << which));
238            }
239            END
240        }
241
242        void postAndWait2_1(coroutine_type::self& self)
243        {
244            BEGIN
245            {
246                LLEventWithID pair = ::postAndWait2(self,
247                                                    LLSD().insert("value", 18).insert("fail", LLSD()),
248                                                    immediateAPI.getPump(),
249                                                    "reply2",
250                                                    "error2",
251                                                    "reply",
252                                                    "error");
253                result = pair.first;
254                which  = pair.second;
255                debug(STRINGIZE("result = " << result << ", which = " << which));
256            }
257            END
258        }
259
260        void coroPump(coroutine_type::self& self)
261        {
262            BEGIN
263            {
264                LLCoroEventPump waiter;
265                replyName = waiter.getName();
266                result = waiter.wait(self);
267            }
268            END
269        }
270
271        void coroPumpPost(coroutine_type::self& self)
272        {
273            BEGIN
274            {
275                LLCoroEventPump waiter;
276                result = waiter.postAndWait(self, LLSD().insert("value", 17),
277                                            immediateAPI.getPump(), "reply");
278            }
279            END
280        }
281
282        void coroPumps(coroutine_type::self& self)
283        {
284            BEGIN
285            {
286                LLCoroEventPumps waiter;
287                replyName = waiter.getName0();
288                errorName = waiter.getName1();
289                LLEventWithID pair(waiter.wait(self));
290                result = pair.first;
291                which  = pair.second;
292            }
293            END
294        }
295
296        void coroPumpsNoEx(coroutine_type::self& self)
297        {
298            BEGIN
299            {
300                LLCoroEventPumps waiter;
301                replyName = waiter.getName0();
302                errorName = waiter.getName1();
303                result = waiter.waitWithException(self);
304            }
305            END
306        }
307
308        void coroPumpsEx(coroutine_type::self& self)
309        {
310            BEGIN
311            {
312                LLCoroEventPumps waiter;
313                replyName = waiter.getName0();
314                errorName = waiter.getName1();
315                try
316                {
317                    result = waiter.waitWithException(self);
318                    debug("no exception");
319                }
320                catch (const LLErrorEvent& e)
321                {
322                    debug(STRINGIZE("exception " << e.what()));
323                    errordata = e.getData();
324                }
325            }
326            END
327        }
328
329        void coroPumpsNoLog(coroutine_type::self& self)
330        {
331            BEGIN
332            {
333                LLCoroEventPumps waiter;
334                replyName = waiter.getName0();
335                errorName = waiter.getName1();
336                result = waiter.waitWithLog(self);
337            }
338            END
339        }
340
341        void coroPumpsLog(coroutine_type::self& self)
342        {
343            BEGIN
344            {
345                LLCoroEventPumps waiter;
346                replyName = waiter.getName0();
347                errorName = waiter.getName1();
348                WrapLL_ERRS capture;
349                try
350                {
351                    result = waiter.waitWithLog(self);
352                    debug("no exception");
353                }
354                catch (const WrapLL_ERRS::FatalException& e)
355                {
356                    debug(STRINGIZE("exception " << e.what()));
357                    threw = e.what();
358                }
359            }
360            END
361        }
362
363        void coroPumpsPost(coroutine_type::self& self)
364        {
365            BEGIN
366            {
367                LLCoroEventPumps waiter;
368                LLEventWithID pair(waiter.postAndWait(self, LLSD().insert("value", 23),
369                                                      immediateAPI.getPump(), "reply", "error"));
370                result = pair.first;
371                which  = pair.second;
372            }
373            END
374        }
375
376        void coroPumpsPost_1(coroutine_type::self& self)
377        {
378            BEGIN
379            {
380                LLCoroEventPumps waiter;
381                LLEventWithID pair(
382                    waiter.postAndWait(self, LLSD().insert("value", 23).insert("fail", LLSD()),
383                                       immediateAPI.getPump(), "reply", "error"));
384                result = pair.first;
385                which  = pair.second;
386            }
387            END
388        }
389
390        void coroPumpsPostNoEx(coroutine_type::self& self)
391        {
392            BEGIN
393            {
394                LLCoroEventPumps waiter;
395                result = waiter.postAndWaitWithException(self, LLSD().insert("value", 8),
396                                                         immediateAPI.getPump(), "reply", "error");
397            }
398            END
399        }
400
401        void coroPumpsPostEx(coroutine_type::self& self)
402        {
403            BEGIN
404            {
405                LLCoroEventPumps waiter;
406                try
407                {
408                    result = waiter.postAndWaitWithException(self,
409                        LLSD().insert("value", 9).insert("fail", LLSD()),
410                        immediateAPI.getPump(), "reply", "error");
411                    debug("no exception");
412                }
413                catch (const LLErrorEvent& e)
414                {
415                    debug(STRINGIZE("exception " << e.what()));
416                    errordata = e.getData();
417                }
418            }
419            END
420        }
421
422        void coroPumpsPostNoLog(coroutine_type::self& self)
423        {
424            BEGIN
425            {
426                LLCoroEventPumps waiter;
427                result = waiter.postAndWaitWithLog(self, LLSD().insert("value", 30),
428                                                   immediateAPI.getPump(), "reply", "error");
429            }
430            END
431        }
432
433        void coroPumpsPostLog(coroutine_type::self& self)
434        {
435            BEGIN
436            {
437                LLCoroEventPumps waiter;
438                WrapLL_ERRS capture;
439                try
440                {
441                    result = waiter.postAndWaitWithLog(self,
442                        LLSD().insert("value", 31).insert("fail", LLSD()),
443                        immediateAPI.getPump(), "reply", "error");
444                    debug("no exception");
445                }
446                catch (const WrapLL_ERRS::FatalException& e)
447                {
448                    debug(STRINGIZE("exception " << e.what()));
449                    threw = e.what();
450                }
451            }
452            END
453        }
454
455        void ensure_done(coroutine_type& coro)
456        {
457            ensure("coroutine complete", ! coro);
458        }
459
460        ImmediateAPI immediateAPI;
461        std::string replyName, errorName, threw;
462        LLSD result, errordata;
463        int which;
464    };
465    typedef test_group<coroutine_data> coroutine_group;
466    typedef coroutine_group::object object;
467    coroutine_group coroutinegrp("coroutine");
468
469    template<> template<>
470    void object::test<1>()
471    {
472        set_test_name("From banana.cpp example program in Boost.Coroutine distro");
473        std::string buffer = "banananana"; 
474        std::string match = "nana"; 
475        std::string::iterator begin = buffer.begin();
476        std::string::iterator end = buffer.end();
477
478#if defined(BOOST_CORO_POSIX_IMPL)
479//      std::cout << "Using Boost.Coroutine " << BOOST_CORO_POSIX_IMPL << '\n';
480#else
481//      std::cout << "Using non-Posix Boost.Coroutine implementation" << std::endl;
482#endif
483
484        typedef std::string::iterator signature(std::string::iterator, 
485                                                std::string::iterator, 
486                                                std::string,
487                                                match_coroutine_type::self&);
488
489        coroutine<std::string::iterator(void)> matcher
490            (boost::bind(static_cast<signature*>(match_substring), 
491                         begin, 
492                         end, 
493                         match, 
494                         _1)); 
495
496        std::string::iterator i = matcher();
497/*==========================================================================*|
498        while(matcher && i != buffer.end()) {
499            std::cout <<"Match at: "<< std::distance(buffer.begin(), i)<<'\n'; 
500            i = matcher();
501        }
502|*==========================================================================*/
503        size_t matches[] = { 2, 4, 6 };
504        for (size_t *mi(boost::begin(matches)), *mend(boost::end(matches));
505             mi != mend; ++mi, i = matcher())
506        {
507            ensure("more", matcher);
508            ensure("found", i != buffer.end());
509            ensure_equals("value", std::distance(buffer.begin(), i), *mi);
510        }
511        ensure("done", ! matcher);
512    }
513
514    template<> template<>
515    void object::test<2>()
516    {
517        set_test_name("explicit_wait");
518        DEBUG;
519
520        // Construct the coroutine instance that will run explicit_wait.
521        // Pass the ctor a callable that accepts the coroutine_type::self
522        // param passed by the library.
523        coroutine_type coro(boost::bind(&coroutine_data::explicit_wait, this, _1));
524        // Start the coroutine
525        coro(std::nothrow);
526        // When the coroutine waits for the event pump, it returns here.
527        debug("about to send");
528        // Satisfy the wait.
529        LLEventPumps::instance().obtain("source").post("received");
530        // Now wait for the coroutine to complete.
531        ensure_done(coro);
532        // ensure the coroutine ran and woke up again with the intended result
533        ensure_equals(result.asString(), "received");
534    }
535
536    template<> template<>
537    void object::test<3>()
538    {
539        set_test_name("waitForEventOn1");
540        DEBUG;
541        coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn1, this, _1));
542        coro(std::nothrow);
543        debug("about to send");
544        LLEventPumps::instance().obtain("source").post("received");
545        debug("back from send");
546        ensure_done(coro);
547        ensure_equals(result.asString(), "received");
548    }
549
550    template<> template<>
551    void object::test<4>()
552    {
553        set_test_name("waitForEventOn2 reply");
554        {
555        DEBUG;
556        coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn2, this, _1));
557        coro(std::nothrow);
558        debug("about to send");
559        LLEventPumps::instance().obtain("reply").post("received");
560        debug("back from send");
561        ensure_done(coro);
562        }
563        ensure_equals(result.asString(), "received");
564        ensure_equals("which pump", which, 0);
565    }
566
567    template<> template<>
568    void object::test<5>()
569    {
570        set_test_name("waitForEventOn2 error");
571        DEBUG;
572        coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn2, this, _1));
573        coro(std::nothrow);
574        debug("about to send");
575        LLEventPumps::instance().obtain("error").post("badness");
576        debug("back from send");
577        ensure_done(coro);
578        ensure_equals(result.asString(), "badness");
579        ensure_equals("which pump", which, 1);
580    }
581
582    template<> template<>
583    void object::test<6>()
584    {
585        set_test_name("coroPump");
586        DEBUG;
587        coroutine_type coro(boost::bind(&coroutine_data::coroPump, this, _1));
588        coro(std::nothrow);
589        debug("about to send");
590        LLEventPumps::instance().obtain(replyName).post("received");
591        debug("back from send");
592        ensure_done(coro);
593        ensure_equals(result.asString(), "received");
594    }
595
596    template<> template<>
597    void object::test<7>()
598    {
599        set_test_name("coroPumps reply");
600        DEBUG;
601        coroutine_type coro(boost::bind(&coroutine_data::coroPumps, this, _1));
602        coro(std::nothrow);
603        debug("about to send");
604        LLEventPumps::instance().obtain(replyName).post("received");
605        debug("back from send");
606        ensure_done(coro);
607        ensure_equals(result.asString(), "received");
608        ensure_equals("which pump", which, 0);
609    }
610
611    template<> template<>
612    void object::test<8>()
613    {
614        set_test_name("coroPumps error");
615        DEBUG;
616        coroutine_type coro(boost::bind(&coroutine_data::coroPumps, this, _1));
617        coro(std::nothrow);
618        debug("about to send");
619        LLEventPumps::instance().obtain(errorName).post("badness");
620        debug("back from send");
621        ensure_done(coro);
622        ensure_equals(result.asString(), "badness");
623        ensure_equals("which pump", which, 1);
624    }
625
626    template<> template<>
627    void object::test<9>()
628    {
629        set_test_name("coroPumpsNoEx");
630        DEBUG;
631        coroutine_type coro(boost::bind(&coroutine_data::coroPumpsNoEx, this, _1));
632        coro(std::nothrow);
633        debug("about to send");
634        LLEventPumps::instance().obtain(replyName).post("received");
635        debug("back from send");
636        ensure_done(coro);
637        ensure_equals(result.asString(), "received");
638    }
639
640    template<> template<>
641    void object::test<10>()
642    {
643        set_test_name("coroPumpsEx");
644        DEBUG;
645        coroutine_type coro(boost::bind(&coroutine_data::coroPumpsEx, this, _1));
646        coro(std::nothrow);
647        debug("about to send");
648        LLEventPumps::instance().obtain(errorName).post("badness");
649        debug("back from send");
650        ensure_done(coro);
651        ensure("no result", result.isUndefined());
652        ensure_equals("got error", errordata.asString(), "badness");
653    }
654
655    template<> template<>
656    void object::test<11>()
657    {
658        set_test_name("coroPumpsNoLog");
659        DEBUG;
660        coroutine_type coro(boost::bind(&coroutine_data::coroPumpsNoLog, this, _1));
661        coro(std::nothrow);
662        debug("about to send");
663        LLEventPumps::instance().obtain(replyName).post("received");
664        debug("back from send");
665        ensure_done(coro);
666        ensure_equals(result.asString(), "received");
667    }
668
669    template<> template<>
670    void object::test<12>()
671    {
672        set_test_name("coroPumpsLog");
673        DEBUG;
674        coroutine_type coro(boost::bind(&coroutine_data::coroPumpsLog, this, _1));
675        coro(std::nothrow);
676        debug("about to send");
677        LLEventPumps::instance().obtain(errorName).post("badness");
678        debug("back from send");
679        ensure_done(coro);
680        ensure("no result", result.isUndefined());
681        ensure_contains("got error", threw, "badness");
682    }
683
684    template<> template<>
685    void object::test<13>()
686    {
687        set_test_name("postAndWait1");
688        DEBUG;
689        coroutine_type coro(boost::bind(&coroutine_data::postAndWait1, this, _1));
690        coro(std::nothrow);
691        ensure_done(coro);
692        ensure_equals(result.asInteger(), 18);
693    }
694
695    template<> template<>
696    void object::test<14>()
697    {
698        set_test_name("postAndWait2");
699        DEBUG;
700        coroutine_type coro(boost::bind(&coroutine_data::postAndWait2, this, _1));
701        coro(std::nothrow);
702        ensure_done(coro);
703        ensure_equals(result.asInteger(), 19);
704        ensure_equals(which, 0);
705    }
706
707    template<> template<>
708    void object::test<15>()
709    {
710        set_test_name("postAndWait2_1");
711        DEBUG;
712        coroutine_type coro(boost::bind(&coroutine_data::postAndWait2_1, this, _1));
713        coro(std::nothrow);
714        ensure_done(coro);
715        ensure_equals(result.asInteger(), 19);
716        ensure_equals(which, 1);
717    }
718
719    template<> template<>
720    void object::test<16>()
721    {
722        set_test_name("coroPumpPost");
723        DEBUG;
724        coroutine_type coro(boost::bind(&coroutine_data::coroPumpPost, this, _1));
725        coro(std::nothrow);
726        ensure_done(coro);
727        ensure_equals(result.asInteger(), 18);
728    }
729
730    template<> template<>
731    void object::test<17>()
732    {
733        set_test_name("coroPumpsPost reply");
734        DEBUG;
735        coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPost, this, _1));
736        coro(std::nothrow);
737        ensure_done(coro);
738        ensure_equals(result.asInteger(), 24);
739        ensure_equals("which pump", which, 0);
740    }
741
742    template<> template<>
743    void object::test<18>()
744    {
745        set_test_name("coroPumpsPost error");
746        DEBUG;
747        coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPost_1, this, _1));
748        coro(std::nothrow);
749        ensure_done(coro);
750        ensure_equals(result.asInteger(), 24);
751        ensure_equals("which pump", which, 1);
752    }
753
754    template<> template<>
755    void object::test<19>()
756    {
757        set_test_name("coroPumpsPostNoEx");
758        DEBUG;
759        coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostNoEx, this, _1));
760        coro(std::nothrow);
761        ensure_done(coro);
762        ensure_equals(result.asInteger(), 9);
763    }
764
765    template<> template<>
766    void object::test<20>()
767    {
768        set_test_name("coroPumpsPostEx");
769        DEBUG;
770        coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostEx, this, _1));
771        coro(std::nothrow);
772        ensure_done(coro);
773        ensure("no result", result.isUndefined());
774        ensure_equals("got error", errordata.asInteger(), 10);
775    }
776
777    template<> template<>
778    void object::test<21>()
779    {
780        set_test_name("coroPumpsPostNoLog");
781        DEBUG;
782        coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostNoLog, this, _1));
783        coro(std::nothrow);
784        ensure_done(coro);
785        ensure_equals(result.asInteger(), 31);
786    }
787
788    template<> template<>
789    void object::test<22>()
790    {
791        set_test_name("coroPumpsPostLog");
792        DEBUG;
793        coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostLog, this, _1));
794        coro(std::nothrow);
795        ensure_done(coro);
796        ensure("no result", result.isUndefined());
797        ensure_contains("got error", threw, "32");
798    }
799} // namespace tut