00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037 #include "gmock/gmock-spec-builders.h"
00038
00039 #include <stdlib.h>
00040 #include <iostream>
00041 #include <map>
00042 #include <set>
00043 #include <string>
00044 #include "gmock/gmock.h"
00045 #include "gtest/gtest.h"
00046
00047 #if GTEST_OS_CYGWIN || GTEST_OS_LINUX || GTEST_OS_MAC
00048 # include <unistd.h>
00049 #endif
00050
00051 namespace testing {
00052 namespace internal {
00053
00054
00055
00056 GTEST_API_ GTEST_DEFINE_STATIC_MUTEX_(g_gmock_mutex);
00057
00058
00059 GTEST_API_ void LogWithLocation(testing::internal::LogSeverity severity,
00060 const char* file, int line,
00061 const string& message) {
00062 ::std::ostringstream s;
00063 s << file << ":" << line << ": " << message << ::std::endl;
00064 Log(severity, s.str(), 0);
00065 }
00066
00067
00068 ExpectationBase::ExpectationBase(const char* a_file,
00069 int a_line,
00070 const string& a_source_text)
00071 : file_(a_file),
00072 line_(a_line),
00073 source_text_(a_source_text),
00074 cardinality_specified_(false),
00075 cardinality_(Exactly(1)),
00076 call_count_(0),
00077 retired_(false),
00078 extra_matcher_specified_(false),
00079 repeated_action_specified_(false),
00080 retires_on_saturation_(false),
00081 last_clause_(kNone),
00082 action_count_checked_(false) {}
00083
00084
00085 ExpectationBase::~ExpectationBase() {}
00086
00087
00088
00089 void ExpectationBase::SpecifyCardinality(const Cardinality& a_cardinality) {
00090 cardinality_specified_ = true;
00091 cardinality_ = a_cardinality;
00092 }
00093
00094
00095 void ExpectationBase::RetireAllPreRequisites()
00096 GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
00097 if (is_retired()) {
00098
00099
00100 return;
00101 }
00102
00103 for (ExpectationSet::const_iterator it = immediate_prerequisites_.begin();
00104 it != immediate_prerequisites_.end(); ++it) {
00105 ExpectationBase* const prerequisite = it->expectation_base().get();
00106 if (!prerequisite->is_retired()) {
00107 prerequisite->RetireAllPreRequisites();
00108 prerequisite->Retire();
00109 }
00110 }
00111 }
00112
00113
00114
00115 bool ExpectationBase::AllPrerequisitesAreSatisfied() const
00116 GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
00117 g_gmock_mutex.AssertHeld();
00118 for (ExpectationSet::const_iterator it = immediate_prerequisites_.begin();
00119 it != immediate_prerequisites_.end(); ++it) {
00120 if (!(it->expectation_base()->IsSatisfied()) ||
00121 !(it->expectation_base()->AllPrerequisitesAreSatisfied()))
00122 return false;
00123 }
00124 return true;
00125 }
00126
00127
00128 void ExpectationBase::FindUnsatisfiedPrerequisites(ExpectationSet* result) const
00129 GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
00130 g_gmock_mutex.AssertHeld();
00131 for (ExpectationSet::const_iterator it = immediate_prerequisites_.begin();
00132 it != immediate_prerequisites_.end(); ++it) {
00133 if (it->expectation_base()->IsSatisfied()) {
00134
00135
00136 if (it->expectation_base()->call_count_ == 0) {
00137 it->expectation_base()->FindUnsatisfiedPrerequisites(result);
00138 }
00139 } else {
00140
00141
00142
00143 *result += *it;
00144 }
00145 }
00146 }
00147
00148
00149
00150 void ExpectationBase::DescribeCallCountTo(::std::ostream* os) const
00151 GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
00152 g_gmock_mutex.AssertHeld();
00153
00154
00155 *os << " Expected: to be ";
00156 cardinality().DescribeTo(os);
00157 *os << "\n Actual: ";
00158 Cardinality::DescribeActualCallCountTo(call_count(), os);
00159
00160
00161
00162 *os << " - " << (IsOverSaturated() ? "over-saturated" :
00163 IsSaturated() ? "saturated" :
00164 IsSatisfied() ? "satisfied" : "unsatisfied")
00165 << " and "
00166 << (is_retired() ? "retired" : "active");
00167 }
00168
00169
00170
00171
00172
00173 void ExpectationBase::CheckActionCountIfNotDone() const
00174 GTEST_LOCK_EXCLUDED_(mutex_) {
00175 bool should_check = false;
00176 {
00177 MutexLock l(&mutex_);
00178 if (!action_count_checked_) {
00179 action_count_checked_ = true;
00180 should_check = true;
00181 }
00182 }
00183
00184 if (should_check) {
00185 if (!cardinality_specified_) {
00186
00187
00188 return;
00189 }
00190
00191
00192 const int action_count = static_cast<int>(untyped_actions_.size());
00193 const int upper_bound = cardinality().ConservativeUpperBound();
00194 const int lower_bound = cardinality().ConservativeLowerBound();
00195 bool too_many;
00196
00197 if (action_count > upper_bound ||
00198 (action_count == upper_bound && repeated_action_specified_)) {
00199 too_many = true;
00200 } else if (0 < action_count && action_count < lower_bound &&
00201 !repeated_action_specified_) {
00202 too_many = false;
00203 } else {
00204 return;
00205 }
00206
00207 ::std::stringstream ss;
00208 DescribeLocationTo(&ss);
00209 ss << "Too " << (too_many ? "many" : "few")
00210 << " actions specified in " << source_text() << "...\n"
00211 << "Expected to be ";
00212 cardinality().DescribeTo(&ss);
00213 ss << ", but has " << (too_many ? "" : "only ")
00214 << action_count << " WillOnce()"
00215 << (action_count == 1 ? "" : "s");
00216 if (repeated_action_specified_) {
00217 ss << " and a WillRepeatedly()";
00218 }
00219 ss << ".";
00220 Log(kWarning, ss.str(), -1);
00221 }
00222 }
00223
00224
00225 void ExpectationBase::UntypedTimes(const Cardinality& a_cardinality) {
00226 if (last_clause_ == kTimes) {
00227 ExpectSpecProperty(false,
00228 ".Times() cannot appear "
00229 "more than once in an EXPECT_CALL().");
00230 } else {
00231 ExpectSpecProperty(last_clause_ < kTimes,
00232 ".Times() cannot appear after "
00233 ".InSequence(), .WillOnce(), .WillRepeatedly(), "
00234 "or .RetiresOnSaturation().");
00235 }
00236 last_clause_ = kTimes;
00237
00238 SpecifyCardinality(a_cardinality);
00239 }
00240
00241
00242
00243 GTEST_API_ ThreadLocal<Sequence*> g_gmock_implicit_sequence;
00244
00245
00246
00247 void ReportUninterestingCall(CallReaction reaction, const string& msg) {
00248 switch (reaction) {
00249 case kAllow:
00250 Log(kInfo, msg, 3);
00251 break;
00252 case kWarn:
00253 Log(kWarning, msg, 3);
00254 break;
00255 default:
00256 Expect(false, NULL, -1, msg);
00257 }
00258 }
00259
00260 UntypedFunctionMockerBase::UntypedFunctionMockerBase()
00261 : mock_obj_(NULL), name_("") {}
00262
00263 UntypedFunctionMockerBase::~UntypedFunctionMockerBase() {}
00264
00265
00266
00267
00268
00269 void UntypedFunctionMockerBase::RegisterOwner(const void* mock_obj)
00270 GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
00271 {
00272 MutexLock l(&g_gmock_mutex);
00273 mock_obj_ = mock_obj;
00274 }
00275 Mock::Register(mock_obj, this);
00276 }
00277
00278
00279
00280
00281 void UntypedFunctionMockerBase::SetOwnerAndName(const void* mock_obj,
00282 const char* name)
00283 GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
00284
00285
00286 MutexLock l(&g_gmock_mutex);
00287 mock_obj_ = mock_obj;
00288 name_ = name;
00289 }
00290
00291
00292
00293 const void* UntypedFunctionMockerBase::MockObject() const
00294 GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
00295 const void* mock_obj;
00296 {
00297
00298
00299 MutexLock l(&g_gmock_mutex);
00300 Assert(mock_obj_ != NULL, __FILE__, __LINE__,
00301 "MockObject() must not be called before RegisterOwner() or "
00302 "SetOwnerAndName() has been called.");
00303 mock_obj = mock_obj_;
00304 }
00305 return mock_obj;
00306 }
00307
00308
00309
00310 const char* UntypedFunctionMockerBase::Name() const
00311 GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
00312 const char* name;
00313 {
00314
00315
00316 MutexLock l(&g_gmock_mutex);
00317 Assert(name_ != NULL, __FILE__, __LINE__,
00318 "Name() must not be called before SetOwnerAndName() has "
00319 "been called.");
00320 name = name_;
00321 }
00322 return name;
00323 }
00324
00325
00326
00327
00328 const UntypedActionResultHolderBase*
00329 UntypedFunctionMockerBase::UntypedInvokeWith(const void* const untyped_args)
00330 GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
00331 if (untyped_expectations_.size() == 0) {
00332
00333
00334
00335
00336
00337
00338
00339 const CallReaction reaction =
00340 Mock::GetReactionOnUninterestingCalls(MockObject());
00341
00342
00343
00344
00345 const bool need_to_report_uninteresting_call =
00346
00347
00348 reaction == kAllow ? LogIsVisible(kInfo) :
00349
00350
00351 reaction == kWarn ? LogIsVisible(kWarning) :
00352
00353
00354 true;
00355
00356 if (!need_to_report_uninteresting_call) {
00357
00358 return this->UntypedPerformDefaultAction(untyped_args, "");
00359 }
00360
00361
00362 ::std::stringstream ss;
00363 this->UntypedDescribeUninterestingCall(untyped_args, &ss);
00364
00365
00366 const UntypedActionResultHolderBase* const result =
00367 this->UntypedPerformDefaultAction(untyped_args, ss.str());
00368
00369
00370 if (result != NULL)
00371 result->PrintAsActionResult(&ss);
00372
00373 ReportUninterestingCall(reaction, ss.str());
00374 return result;
00375 }
00376
00377 bool is_excessive = false;
00378 ::std::stringstream ss;
00379 ::std::stringstream why;
00380 ::std::stringstream loc;
00381 const void* untyped_action = NULL;
00382
00383
00384
00385 const ExpectationBase* const untyped_expectation =
00386 this->UntypedFindMatchingExpectation(
00387 untyped_args, &untyped_action, &is_excessive,
00388 &ss, &why);
00389 const bool found = untyped_expectation != NULL;
00390
00391
00392
00393
00394 const bool need_to_report_call =
00395 !found || is_excessive || LogIsVisible(kInfo);
00396 if (!need_to_report_call) {
00397
00398 return
00399 untyped_action == NULL ?
00400 this->UntypedPerformDefaultAction(untyped_args, "") :
00401 this->UntypedPerformAction(untyped_action, untyped_args);
00402 }
00403
00404 ss << " Function call: " << Name();
00405 this->UntypedPrintArgs(untyped_args, &ss);
00406
00407
00408
00409 if (found && !is_excessive) {
00410 untyped_expectation->DescribeLocationTo(&loc);
00411 }
00412
00413 const UntypedActionResultHolderBase* const result =
00414 untyped_action == NULL ?
00415 this->UntypedPerformDefaultAction(untyped_args, ss.str()) :
00416 this->UntypedPerformAction(untyped_action, untyped_args);
00417 if (result != NULL)
00418 result->PrintAsActionResult(&ss);
00419 ss << "\n" << why.str();
00420
00421 if (!found) {
00422
00423 Expect(false, NULL, -1, ss.str());
00424 } else if (is_excessive) {
00425
00426 Expect(false, untyped_expectation->file(),
00427 untyped_expectation->line(), ss.str());
00428 } else {
00429
00430
00431 Log(kInfo, loc.str() + ss.str(), 2);
00432 }
00433
00434 return result;
00435 }
00436
00437
00438
00439 Expectation UntypedFunctionMockerBase::GetHandleOf(ExpectationBase* exp) {
00440 for (UntypedExpectations::const_iterator it =
00441 untyped_expectations_.begin();
00442 it != untyped_expectations_.end(); ++it) {
00443 if (it->get() == exp) {
00444 return Expectation(*it);
00445 }
00446 }
00447
00448 Assert(false, __FILE__, __LINE__, "Cannot find expectation.");
00449 return Expectation();
00450
00451
00452 }
00453
00454
00455
00456
00457 bool UntypedFunctionMockerBase::VerifyAndClearExpectationsLocked()
00458 GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
00459 g_gmock_mutex.AssertHeld();
00460 bool expectations_met = true;
00461 for (UntypedExpectations::const_iterator it =
00462 untyped_expectations_.begin();
00463 it != untyped_expectations_.end(); ++it) {
00464 ExpectationBase* const untyped_expectation = it->get();
00465 if (untyped_expectation->IsOverSaturated()) {
00466
00467
00468
00469 expectations_met = false;
00470 } else if (!untyped_expectation->IsSatisfied()) {
00471 expectations_met = false;
00472 ::std::stringstream ss;
00473 ss << "Actual function call count doesn't match "
00474 << untyped_expectation->source_text() << "...\n";
00475
00476
00477
00478 untyped_expectation->MaybeDescribeExtraMatcherTo(&ss);
00479 untyped_expectation->DescribeCallCountTo(&ss);
00480 Expect(false, untyped_expectation->file(),
00481 untyped_expectation->line(), ss.str());
00482 }
00483 }
00484
00485
00486
00487
00488
00489
00490
00491
00492 UntypedExpectations expectations_to_delete;
00493 untyped_expectations_.swap(expectations_to_delete);
00494
00495 g_gmock_mutex.Unlock();
00496 expectations_to_delete.clear();
00497 g_gmock_mutex.Lock();
00498
00499 return expectations_met;
00500 }
00501
00502 }
00503
00504
00505
00506 namespace {
00507
00508 typedef std::set<internal::UntypedFunctionMockerBase*> FunctionMockers;
00509
00510
00511
00512
00513 struct MockObjectState {
00514 MockObjectState()
00515 : first_used_file(NULL), first_used_line(-1), leakable(false) {}
00516
00517
00518
00519 const char* first_used_file;
00520 int first_used_line;
00521 ::std::string first_used_test_case;
00522 ::std::string first_used_test;
00523 bool leakable;
00524 FunctionMockers function_mockers;
00525 };
00526
00527
00528
00529
00530
00531 class MockObjectRegistry {
00532 public:
00533
00534 typedef std::map<const void*, MockObjectState> StateMap;
00535
00536
00537
00538
00539
00540 ~MockObjectRegistry() {
00541
00542
00543
00544 if (!GMOCK_FLAG(catch_leaked_mocks))
00545 return;
00546
00547 int leaked_count = 0;
00548 for (StateMap::const_iterator it = states_.begin(); it != states_.end();
00549 ++it) {
00550 if (it->second.leakable)
00551 continue;
00552
00553
00554
00555 std::cout << "\n";
00556 const MockObjectState& state = it->second;
00557 std::cout << internal::FormatFileLocation(state.first_used_file,
00558 state.first_used_line);
00559 std::cout << " ERROR: this mock object";
00560 if (state.first_used_test != "") {
00561 std::cout << " (used in test " << state.first_used_test_case << "."
00562 << state.first_used_test << ")";
00563 }
00564 std::cout << " should be deleted but never is. Its address is @"
00565 << it->first << ".";
00566 leaked_count++;
00567 }
00568 if (leaked_count > 0) {
00569 std::cout << "\nERROR: " << leaked_count
00570 << " leaked mock " << (leaked_count == 1 ? "object" : "objects")
00571 << " found at program exit.\n";
00572 std::cout.flush();
00573 ::std::cerr.flush();
00574
00575
00576
00577 _exit(1);
00578
00579 }
00580 }
00581
00582 StateMap& states() { return states_; }
00583
00584 private:
00585 StateMap states_;
00586 };
00587
00588
00589 MockObjectRegistry g_mock_object_registry;
00590
00591
00592
00593 std::map<const void*, internal::CallReaction> g_uninteresting_call_reaction;
00594
00595
00596
00597 void SetReactionOnUninterestingCalls(const void* mock_obj,
00598 internal::CallReaction reaction)
00599 GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
00600 internal::MutexLock l(&internal::g_gmock_mutex);
00601 g_uninteresting_call_reaction[mock_obj] = reaction;
00602 }
00603
00604 }
00605
00606
00607
00608 void Mock::AllowUninterestingCalls(const void* mock_obj)
00609 GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
00610 SetReactionOnUninterestingCalls(mock_obj, internal::kAllow);
00611 }
00612
00613
00614
00615 void Mock::WarnUninterestingCalls(const void* mock_obj)
00616 GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
00617 SetReactionOnUninterestingCalls(mock_obj, internal::kWarn);
00618 }
00619
00620
00621
00622 void Mock::FailUninterestingCalls(const void* mock_obj)
00623 GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
00624 SetReactionOnUninterestingCalls(mock_obj, internal::kFail);
00625 }
00626
00627
00628
00629 void Mock::UnregisterCallReaction(const void* mock_obj)
00630 GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
00631 internal::MutexLock l(&internal::g_gmock_mutex);
00632 g_uninteresting_call_reaction.erase(mock_obj);
00633 }
00634
00635
00636
00637 internal::CallReaction Mock::GetReactionOnUninterestingCalls(
00638 const void* mock_obj)
00639 GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
00640 internal::MutexLock l(&internal::g_gmock_mutex);
00641 return (g_uninteresting_call_reaction.count(mock_obj) == 0) ?
00642 internal::kDefault : g_uninteresting_call_reaction[mock_obj];
00643 }
00644
00645
00646
00647 void Mock::AllowLeak(const void* mock_obj)
00648 GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
00649 internal::MutexLock l(&internal::g_gmock_mutex);
00650 g_mock_object_registry.states()[mock_obj].leakable = true;
00651 }
00652
00653
00654
00655
00656 bool Mock::VerifyAndClearExpectations(void* mock_obj)
00657 GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
00658 internal::MutexLock l(&internal::g_gmock_mutex);
00659 return VerifyAndClearExpectationsLocked(mock_obj);
00660 }
00661
00662
00663
00664
00665 bool Mock::VerifyAndClear(void* mock_obj)
00666 GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
00667 internal::MutexLock l(&internal::g_gmock_mutex);
00668 ClearDefaultActionsLocked(mock_obj);
00669 return VerifyAndClearExpectationsLocked(mock_obj);
00670 }
00671
00672
00673
00674
00675 bool Mock::VerifyAndClearExpectationsLocked(void* mock_obj)
00676 GTEST_EXCLUSIVE_LOCK_REQUIRED_(internal::g_gmock_mutex) {
00677 internal::g_gmock_mutex.AssertHeld();
00678 if (g_mock_object_registry.states().count(mock_obj) == 0) {
00679
00680 return true;
00681 }
00682
00683
00684
00685 bool expectations_met = true;
00686 FunctionMockers& mockers =
00687 g_mock_object_registry.states()[mock_obj].function_mockers;
00688 for (FunctionMockers::const_iterator it = mockers.begin();
00689 it != mockers.end(); ++it) {
00690 if (!(*it)->VerifyAndClearExpectationsLocked()) {
00691 expectations_met = false;
00692 }
00693 }
00694
00695
00696
00697 return expectations_met;
00698 }
00699
00700
00701 void Mock::Register(const void* mock_obj,
00702 internal::UntypedFunctionMockerBase* mocker)
00703 GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
00704 internal::MutexLock l(&internal::g_gmock_mutex);
00705 g_mock_object_registry.states()[mock_obj].function_mockers.insert(mocker);
00706 }
00707
00708
00709
00710
00711 void Mock::RegisterUseByOnCallOrExpectCall(const void* mock_obj,
00712 const char* file, int line)
00713 GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
00714 internal::MutexLock l(&internal::g_gmock_mutex);
00715 MockObjectState& state = g_mock_object_registry.states()[mock_obj];
00716 if (state.first_used_file == NULL) {
00717 state.first_used_file = file;
00718 state.first_used_line = line;
00719 const TestInfo* const test_info =
00720 UnitTest::GetInstance()->current_test_info();
00721 if (test_info != NULL) {
00722
00723
00724
00725 state.first_used_test_case = test_info->test_case_name();
00726 state.first_used_test = test_info->name();
00727 }
00728 }
00729 }
00730
00731
00732
00733
00734
00735 void Mock::UnregisterLocked(internal::UntypedFunctionMockerBase* mocker)
00736 GTEST_EXCLUSIVE_LOCK_REQUIRED_(internal::g_gmock_mutex) {
00737 internal::g_gmock_mutex.AssertHeld();
00738 for (MockObjectRegistry::StateMap::iterator it =
00739 g_mock_object_registry.states().begin();
00740 it != g_mock_object_registry.states().end(); ++it) {
00741 FunctionMockers& mockers = it->second.function_mockers;
00742 if (mockers.erase(mocker) > 0) {
00743
00744 if (mockers.empty()) {
00745 g_mock_object_registry.states().erase(it);
00746 }
00747 return;
00748 }
00749 }
00750 }
00751
00752
00753 void Mock::ClearDefaultActionsLocked(void* mock_obj)
00754 GTEST_EXCLUSIVE_LOCK_REQUIRED_(internal::g_gmock_mutex) {
00755 internal::g_gmock_mutex.AssertHeld();
00756
00757 if (g_mock_object_registry.states().count(mock_obj) == 0) {
00758
00759 return;
00760 }
00761
00762
00763
00764 FunctionMockers& mockers =
00765 g_mock_object_registry.states()[mock_obj].function_mockers;
00766 for (FunctionMockers::const_iterator it = mockers.begin();
00767 it != mockers.end(); ++it) {
00768 (*it)->ClearDefaultActionsLocked();
00769 }
00770
00771
00772
00773 }
00774
00775 Expectation::Expectation() {}
00776
00777 Expectation::Expectation(
00778 const internal::linked_ptr<internal::ExpectationBase>& an_expectation_base)
00779 : expectation_base_(an_expectation_base) {}
00780
00781 Expectation::~Expectation() {}
00782
00783
00784 void Sequence::AddExpectation(const Expectation& expectation) const {
00785 if (*last_expectation_ != expectation) {
00786 if (last_expectation_->expectation_base() != NULL) {
00787 expectation.expectation_base()->immediate_prerequisites_
00788 += *last_expectation_;
00789 }
00790 *last_expectation_ = expectation;
00791 }
00792 }
00793
00794
00795 InSequence::InSequence() {
00796 if (internal::g_gmock_implicit_sequence.get() == NULL) {
00797 internal::g_gmock_implicit_sequence.set(new Sequence);
00798 sequence_created_ = true;
00799 } else {
00800 sequence_created_ = false;
00801 }
00802 }
00803
00804
00805
00806 InSequence::~InSequence() {
00807 if (sequence_created_) {
00808 delete internal::g_gmock_implicit_sequence.get();
00809 internal::g_gmock_implicit_sequence.set(NULL);
00810 }
00811 }
00812
00813 }