1 //
2 //  Copyright (C) 2001-2018 Greg Landrum and Rational Discovery LLC
3 //
4 //   @@ All Rights Reserved @@
5 //  This file is part of the RDKit.
6 //  The contents are covered by the terms of the BSD license
7 //  which is included in the file license.txt, found at the root
8 //  of the RDKit source tree.
9 //
10 
11 #include <RDGeneral/test.h>
12 #include <GraphMol/RDKitBase.h>
13 #include <GraphMol/MonomerInfo.h>
14 #include <GraphMol/RDKitQueries.h>
15 #include <RDGeneral/types.h>
16 #include <RDGeneral/RDLog.h>
17 //#include <boost/log/functions.hpp>
18 #include <GraphMol/FileParsers/FileParsers.h>
19 #include <GraphMol/FileParsers/MolWriters.h>
20 #include <GraphMol/SmilesParse/SmilesParse.h>
21 #include <GraphMol/SmilesParse/SmilesWrite.h>
22 #include <GraphMol/SmilesParse/SmartsWrite.h>
23 #include <sstream>
24 #include <iostream>
25 #include <boost/range/iterator_range.hpp>
26 
27 using namespace std;
28 using namespace RDKit;
29 
30 // -------------------------------------------------------------------
testBookmarks(ROMol m)31 void testBookmarks(ROMol m) {
32   int i;
33 
34   // ------------------------
35   // simple bookmark stuff
36   Atom *a1 = m.getAtomWithIdx(1);
37   m.setAtomBookmark(a1, 666);
38   Atom *a2 = m.getAtomWithBookmark(666);
39 
40   TEST_ASSERT(a2->getIdx() == a1->getIdx());
41 
42   bool ok;
43   m.clearAtomBookmark(666);
44 
45   boost::logging::disable_logs("rdApp.error");
46   try {
47     a2 = m.getAtomWithBookmark(666);
48     ok = 0;
49   } catch (...) {
50     ok = 1;
51   }
52   boost::logging::enable_logs("rdApp.error");
53   CHECK_INVARIANT(ok, "atom bookmark not properly cleared");
54 
55   // ------------------------
56   // repeat a bookmark
57   a1 = m.getAtomWithIdx(1);
58   CHECK_INVARIANT(a1->getIdx() == 1, "");
59   m.setAtomBookmark(a1, 666);
60   m.setAtomBookmark(m.getAtomWithIdx(0), 666);
61   a2 = m.getAtomWithBookmark(666);
62   CHECK_INVARIANT(a2->getIdx() == 1, "");
63   CHECK_INVARIANT(a2->getIdx() == a1->getIdx(), "");
64   m.clearAtomBookmark(666, a2);
65   a2 = m.getAtomWithBookmark(666);
66   i = a2->getIdx();
67   CHECK_INVARIANT(i == 0, "");
68   m.clearAtomBookmark(666, a2);
69   boost::logging::disable_logs("rdApp.error");
70   try {
71     a2 = m.getAtomWithBookmark(666);
72     ok = 0;
73   } catch (...) {
74     ok = 1;
75   }
76   boost::logging::enable_logs("rdApp.error");
77   CHECK_INVARIANT(ok, "atom bookmark not properly cleared");
78 
79   // make sure clearAtomBookmark doesn't barf if there's no
80   // such bookmark:
81   m.clearAtomBookmark(777);
82 
83   //----------------------------
84   // now do bond bookmarks
85   Bond *b1 = m.getBondWithIdx(0);
86   m.setBondBookmark(b1, 23);
87   Bond *b2 = m.getBondWithBookmark(23);
88   CHECK_INVARIANT(b2->getIdx() == b1->getIdx(), "");
89 
90   m.clearBondBookmark(23);
91   boost::logging::disable_logs("rdApp.error");
92   try {
93     b2 = m.getBondWithBookmark(23);
94     ok = 0;
95   } catch (...) {
96     ok = 1;
97   }
98   boost::logging::enable_logs("rdApp.error");
99   CHECK_INVARIANT(ok, "bond bookmark not properly cleared");
100 
101   m.setBondBookmark(b1, 23);
102   m.setBondBookmark(m.getBondWithIdx(1), 23);
103   b2 = m.getBondWithBookmark(23);
104   CHECK_INVARIANT(b2->getIdx() == b1->getIdx(), "");
105   m.clearBondBookmark(23, b2);
106   b2 = m.getBondWithBookmark(23);
107   CHECK_INVARIANT(b2->getIdx() == 1, "");
108   m.clearBondBookmark(23, b2);
109   boost::logging::disable_logs("rdApp.error");
110   try {
111     b2 = m.getBondWithBookmark(23);
112     ok = 0;
113   } catch (...) {
114     ok = 1;
115   }
116   boost::logging::enable_logs("rdApp.error");
117   CHECK_INVARIANT(ok, "bond bookmark not properly cleared");
118 
119   // make sure clearAtomBookmark doesn't barf if there's no
120   // such bookmark:
121   m.clearBondBookmark(777);
122 }
123 
testMolProps()124 void testMolProps() {
125   BOOST_LOG(rdInfoLog)
126       << "-----------------------\n Testing Mol Property Caches" << std::endl;
127   RWMol m2;
128   STR_VECT propNames;
129 
130   m2.addAtom(new Atom(6), true, true);
131   m2.addAtom(new Atom(6), true, true);
132   m2.addBond(0, 1, Bond::TRIPLE);
133 
134   CHECK_INVARIANT(!m2.hasProp("prop1"), "");
135   CHECK_INVARIANT(!m2.hasProp("prop2"), "");
136   m2.setProp("prop1", 2);
137   int tmpI;
138   std::string tmpS;
139   CHECK_INVARIANT(m2.hasProp("prop1"), "");
140   m2.getProp("prop1", tmpI);
141   CHECK_INVARIANT(tmpI == 2, "");
142   m2.getProp("prop1", tmpS);
143   CHECK_INVARIANT(tmpS == "2", "");
144   m2.setProp("prop1", std::string("2"));
145   CHECK_INVARIANT(m2.hasProp("prop1"), "");
146   m2.getProp("prop1", tmpS);
147   CHECK_INVARIANT(tmpS == "2", "");
148   std::string tmpString("2");
149   m2.setProp("prop1", tmpString.c_str());
150   CHECK_INVARIANT(m2.hasProp("prop1"), "");
151   m2.getProp("prop1", tmpS);
152   CHECK_INVARIANT(tmpS == "2", "");
153 
154   tmpS = "name";
155   m2.setProp(common_properties::_Name, tmpS);
156 
157   propNames = m2.getPropList(false, false);
158   TEST_ASSERT(propNames.size() == 1);
159   propNames = m2.getPropList(true, false);
160   TEST_ASSERT(propNames.size() == 2);
161 
162   // check for computed properties
163   m2.setProp("cprop1", 1, true);
164   m2.setProp("cprop2", 2, true);
165   STR_VECT cplst;
166   m2.getProp(RDKit::detail::computedPropName, cplst);
167   CHECK_INVARIANT(cplst.size() == 2, "");
168   CHECK_INVARIANT(cplst[0] == "cprop1", "");
169   CHECK_INVARIANT(cplst[1] == "cprop2", "");
170 
171   propNames = m2.getPropList(false, false);
172   TEST_ASSERT(propNames.size() == 1);
173   propNames = m2.getPropList(true, false);
174   TEST_ASSERT(propNames.size() == 2);
175   propNames = m2.getPropList(false, true);
176   TEST_ASSERT(propNames.size() == 3);
177   propNames = m2.getPropList(true, true);
178   TEST_ASSERT(propNames.size() == 5);
179   propNames = m2.getPropList();
180   TEST_ASSERT(propNames.size() == 5);
181 
182   m2.clearProp("cprop1");
183   CHECK_INVARIANT(!m2.hasProp("cprop1"), "");
184   m2.getProp(RDKit::detail::computedPropName, cplst);
185   CHECK_INVARIANT(cplst.size() == 1, "");
186 
187   m2.clearComputedProps();
188   CHECK_INVARIANT(!m2.hasProp("cprop2"), "");
189   m2.getProp(RDKit::detail::computedPropName, cplst);
190   CHECK_INVARIANT(cplst.size() == 0, "");
191 
192   BOOST_LOG(rdInfoLog) << "Finished" << std::endl;
193 }
194 
testClearMol()195 void testClearMol() {
196   BOOST_LOG(rdInfoLog) << "-----------------------\n Testing RWMol.clear()"
197                        << std::endl;
198   RWMol m2;
199 
200   m2.addAtom(new Atom(6), true, true);
201   m2.addAtom(new Atom(6), true, true);
202   m2.addBond(0, 1, Bond::TRIPLE);
203 
204   TEST_ASSERT(!m2.hasProp("prop1"));
205   m2.setProp("prop1", 2);
206   int tmpI;
207   TEST_ASSERT(m2.hasProp("prop1"));
208   m2.getProp("prop1", tmpI);
209   TEST_ASSERT(tmpI == 2);
210 
211   TEST_ASSERT(m2.hasProp(RDKit::detail::computedPropName));
212 
213   m2.clear();
214   TEST_ASSERT(!m2.hasProp("prop1"));
215   TEST_ASSERT(m2.getNumAtoms() == 0);
216   TEST_ASSERT(m2.getNumBonds() == 0);
217   TEST_ASSERT(m2.getAtomBookmarks()->empty());
218   TEST_ASSERT(m2.getBondBookmarks()->empty());
219 
220   TEST_ASSERT(
221       m2.hasProp(RDKit::detail::computedPropName));  // <- github issue 176
222   TEST_ASSERT(m2.getPropList().size() == 1);
223 
224   BOOST_LOG(rdInfoLog) << "Finished" << std::endl;
225 }
226 
testAtomProps()227 void testAtomProps() {
228   BOOST_LOG(rdInfoLog)
229       << "-----------------------\n Testing Atom Property Caches" << std::endl;
230   RWMol m2;
231   m2.addAtom(new Atom(6), true, true);
232   m2.addAtom(new Atom(6), true, true);
233   m2.addBond(0, 1, Bond::TRIPLE);
234 
235   Atom *a1 = m2.getAtomWithIdx(0);
236   Atom *a2 = m2.getAtomWithIdx(0);
237   Atom *a3 = &(*a1);
238   CHECK_INVARIANT(!a1->hasProp("prop1"), "");
239   CHECK_INVARIANT(!a1->hasProp("prop2"), "");
240   CHECK_INVARIANT(!a2->hasProp("prop1"), "");
241   CHECK_INVARIANT(!a2->hasProp("prop2"), "");
242   CHECK_INVARIANT(!a3->hasProp("prop1"), "");
243   CHECK_INVARIANT(!a3->hasProp("prop2"), "");
244   a1->setProp("prop1", 3);
245   a1->setProp("prop2", 4);
246   CHECK_INVARIANT(a1->hasProp("prop1"), "");
247   CHECK_INVARIANT(a1->hasProp("prop2"), "");
248   CHECK_INVARIANT(a2->hasProp("prop1"), "");
249   CHECK_INVARIANT(a2->hasProp("prop2"), "");
250   CHECK_INVARIANT(a3->hasProp("prop1"), "");
251   CHECK_INVARIANT(a3->hasProp("prop2"), "");
252   CHECK_INVARIANT(!a1->hasProp("bogus"), "");
253   CHECK_INVARIANT(!a2->hasProp("bogus"), "");
254   CHECK_INVARIANT(!a3->hasProp("bogus"), "");
255 
256   bool ok = false;
257   a1->setProp<double>("dprop", 4);
258   TEST_ASSERT(a1->hasProp("dprop"));
259   try {
260     a1->getProp<int>("dprop");
261   } catch (const boost::bad_any_cast &) {
262     ok = true;
263   }
264   TEST_ASSERT(ok);
265   a1->setProp<int>("iprop", 4);
266   TEST_ASSERT(a1->hasProp("iprop"));
267   ok = false;
268   try {
269     a1->getProp<double>("iprop");
270   } catch (const boost::bad_any_cast &) {
271     ok = true;
272   }
273   TEST_ASSERT(ok);
274 
275   int tmp;
276   a1->getProp("prop1", tmp);
277   CHECK_INVARIANT(tmp == 3, "");
278   a1->getProp("prop2", tmp);
279   CHECK_INVARIANT(tmp == 4, "");
280   a2->getProp("prop1", tmp);
281   CHECK_INVARIANT(tmp == 3, "");
282   a2->getProp("prop2", tmp);
283   CHECK_INVARIANT(tmp == 4, "");
284   a3->getProp("prop1", tmp);
285   CHECK_INVARIANT(tmp == 3, "");
286   a3->getProp("prop2", tmp);
287   CHECK_INVARIANT(tmp == 4, "");
288 
289   // check for computed properties
290   a1->setProp("cprop1", 1, true);
291   a1->setProp("cprop2", 2, true);
292   STR_VECT cplst;
293   a1->getProp(RDKit::detail::computedPropName, cplst);
294   CHECK_INVARIANT(cplst.size() == 2, "");
295   CHECK_INVARIANT(cplst[0] == "cprop1", "");
296   CHECK_INVARIANT(cplst[1] == "cprop2", "");
297 
298   a1->clearProp("cprop1");
299   CHECK_INVARIANT(!a1->hasProp("cprop1"), "");
300   a1->getProp(RDKit::detail::computedPropName, cplst);
301   CHECK_INVARIANT(cplst.size() == 1, "");
302 
303   a1->clearComputedProps();
304   CHECK_INVARIANT(!a1->hasProp("cprop2"), "");
305   a1->getProp(RDKit::detail::computedPropName, cplst);
306   CHECK_INVARIANT(cplst.size() == 0, "");
307 
308   BOOST_LOG(rdInfoLog) << "Finished" << std::endl;
309 }
310 
testBondProps()311 void testBondProps() {
312   BOOST_LOG(rdInfoLog)
313       << "-----------------------\n Testing Bond Property Caches" << std::endl;
314   RWMol m2;
315   m2.addAtom(new Atom(6), true, true);
316   m2.addAtom(new Atom(6), true, true);
317   m2.addBond(0, 1, Bond::TRIPLE);
318 
319   Bond *b1 = m2.getBondWithIdx(0);
320   Bond *b2 = m2.getBondWithIdx(0);
321   CHECK_INVARIANT(!b1->hasProp("prop1"), "");
322   CHECK_INVARIANT(!b1->hasProp("prop2"), "");
323   CHECK_INVARIANT(!b2->hasProp("prop1"), "");
324   CHECK_INVARIANT(!b2->hasProp("prop2"), "");
325   b1->setProp("prop1", 3);
326   b1->setProp("prop2", 4);
327   CHECK_INVARIANT(b1->hasProp("prop1"), "");
328   CHECK_INVARIANT(b1->hasProp("prop2"), "");
329   CHECK_INVARIANT(b2->hasProp("prop1"), "");
330   CHECK_INVARIANT(b2->hasProp("prop2"), "");
331   CHECK_INVARIANT(!b1->hasProp("bogus"), "");
332   CHECK_INVARIANT(!b2->hasProp("bogus"), "");
333 
334   int tmp;
335   b1->getProp("prop1", tmp);
336   CHECK_INVARIANT(tmp == 3, "");
337   b1->getProp("prop2", tmp);
338   CHECK_INVARIANT(tmp == 4, "");
339   b2->getProp("prop1", tmp);
340   CHECK_INVARIANT(tmp == 3, "");
341   b2->getProp("prop2", tmp);
342   CHECK_INVARIANT(tmp == 4, "");
343 
344   // check for computed properties
345   b1->setProp("cprop1", 1, true);
346   b1->setProp("cprop2", 2, true);
347   STR_VECT cplst;
348   b1->getProp(RDKit::detail::computedPropName, cplst);
349   CHECK_INVARIANT(cplst.size() == 2, "");
350   CHECK_INVARIANT(cplst[0] == "cprop1", "");
351   CHECK_INVARIANT(cplst[1] == "cprop2", "");
352 
353   b1->clearProp("cprop1");
354   CHECK_INVARIANT(!b1->hasProp("cprop1"), "");
355   b1->getProp(RDKit::detail::computedPropName, cplst);
356   CHECK_INVARIANT(cplst.size() == 1, "");
357 
358   b1->clearComputedProps();
359   CHECK_INVARIANT(!b1->hasProp("cprop2"), "");
360   b1->getProp(RDKit::detail::computedPropName, cplst);
361   CHECK_INVARIANT(cplst.size() == 0, "");
362 
363   BOOST_LOG(rdInfoLog) << "Finished" << std::endl;
364 }
365 
366 // this is here because there was at one time a problem with crashes when doing
367 // this stuff
testPropLeak()368 void testPropLeak() {
369   BOOST_LOG(rdInfoLog)
370       << "-----------------------\n Testing Atom and Bond Property Caches"
371       << std::endl;
372   RWMol m2;
373   m2.addAtom(new Atom(6), true, true);
374   m2.addAtom(new Atom(6), true, true);
375   m2.addBond(0, 1, Bond::TRIPLE);
376 
377   Atom *a1 = m2.getAtomWithIdx(0);
378   Atom *a2 = m2.getAtomWithIdx(0);
379   CHECK_INVARIANT(!a1->hasProp("prop1"), "");
380   CHECK_INVARIANT(!a1->hasProp("prop2"), "");
381   CHECK_INVARIANT(!a2->hasProp("prop1"), "");
382   CHECK_INVARIANT(!a2->hasProp("prop2"), "");
383   a1->setProp("prop1", 3);
384   a1->setProp("prop2", 4);
385   CHECK_INVARIANT(a1->hasProp("prop1"), "");
386   CHECK_INVARIANT(a1->hasProp("prop2"), "");
387   CHECK_INVARIANT(a2->hasProp("prop1"), "");
388   CHECK_INVARIANT(a2->hasProp("prop2"), "");
389   CHECK_INVARIANT(!a1->hasProp("bogus"), "");
390   CHECK_INVARIANT(!a2->hasProp("bogus"), "");
391 
392   int tmp;
393   a1->getProp("prop1", tmp);
394   CHECK_INVARIANT(tmp == 3, "");
395   a1->getProp("prop2", tmp);
396   CHECK_INVARIANT(tmp == 4, "");
397   a2->getProp("prop1", tmp);
398   CHECK_INVARIANT(tmp == 3, "");
399   a2->getProp("prop2", tmp);
400   CHECK_INVARIANT(tmp == 4, "");
401 
402   Bond *b1 = m2.getBondWithIdx(0);
403   Bond *b2 = m2.getBondWithIdx(0);
404   CHECK_INVARIANT(!b1->hasProp("prop1"), "");
405   CHECK_INVARIANT(!b1->hasProp("prop2"), "");
406   CHECK_INVARIANT(!b2->hasProp("prop1"), "");
407   CHECK_INVARIANT(!b2->hasProp("prop2"), "");
408   b1->setProp("prop1", 3);
409   b1->setProp("prop2", 4);
410   CHECK_INVARIANT(b1->hasProp("prop1"), "");
411   CHECK_INVARIANT(b1->hasProp("prop2"), "");
412   CHECK_INVARIANT(b2->hasProp("prop1"), "");
413   CHECK_INVARIANT(b2->hasProp("prop2"), "");
414   CHECK_INVARIANT(!b1->hasProp("bogus"), "");
415   CHECK_INVARIANT(!b2->hasProp("bogus"), "");
416 
417   b1->getProp("prop1", tmp);
418   CHECK_INVARIANT(tmp == 3, "");
419   b1->getProp("prop2", tmp);
420   CHECK_INVARIANT(tmp == 4, "");
421   b2->getProp("prop1", tmp);
422   CHECK_INVARIANT(tmp == 3, "");
423   b2->getProp("prop2", tmp);
424   CHECK_INVARIANT(tmp == 4, "");
425 
426   BOOST_LOG(rdInfoLog) << "Finished" << std::endl;
427 }
428 
testMisc()429 void testMisc() {
430   BOOST_LOG(rdInfoLog) << "-----------------------\n Testing Misc Properties"
431                        << std::endl;
432   RWMol m2;
433   m2.addAtom(new Atom(6), true, true);
434   m2.addAtom(new Atom(6), true, true);
435   m2.addAtom(new Atom(6), true, true);
436   m2.addAtom(new Atom(6), true, true);
437   m2.addBond(0, 1, Bond::SINGLE);
438   m2.addBond(1, 2, Bond::SINGLE);
439   m2.addBond(0, 2, Bond::SINGLE);
440   m2.addBond(2, 3, Bond::SINGLE);
441 
442   MolOps::sanitizeMol(m2);
443 
444   Bond *bnd;
445   bnd = m2.getBondBetweenAtoms(0, 1);
446   CHECK_INVARIANT(bnd, "");
447   bnd = m2.getBondBetweenAtoms(1, 0);
448   CHECK_INVARIANT(bnd, "");
449   bnd = m2.getBondBetweenAtoms(3, 0);
450   CHECK_INVARIANT(!bnd, "");
451   bnd = m2.getBondBetweenAtoms(0, 3);
452   CHECK_INVARIANT(!bnd, "");
453   const Bond *cbnd;
454   cbnd = m2.getBondBetweenAtoms(0, 1);
455   CHECK_INVARIANT(cbnd, "");
456   cbnd = m2.getBondBetweenAtoms(1, 0);
457   CHECK_INVARIANT(cbnd, "");
458   cbnd = m2.getBondBetweenAtoms(0, 3);
459   CHECK_INVARIANT(!cbnd, "");
460   cbnd = m2.getBondBetweenAtoms(3, 0);
461   CHECK_INVARIANT(!cbnd, "");
462 
463   CHECK_INVARIANT(m2.getAtomWithIdx(0)->getTotalNumHs() == 2, "");
464 
465   // we'll check atom deletion and handling of bookmarks on deletion
466   // simultaneously:
467   //  (The bookmark thing was the root of Issue 96)
468   m2.setAtomBookmark(m2.getAtomWithIdx(0), 2342);
469   m2.setBondBookmark(m2.getBondWithIdx(0), 2343);
470   m2.removeAtom(static_cast<unsigned int>(0));
471   CHECK_INVARIANT(!m2.hasAtomBookmark(2342), "");
472   CHECK_INVARIANT(!m2.hasBondBookmark(2343), "");
473   CHECK_INVARIANT(m2.getNumAtoms() == 3, "");
474   CHECK_INVARIANT(m2.getNumBonds() == 2, "");
475   MolOps::sanitizeMol(m2);
476   CHECK_INVARIANT(m2.getAtomWithIdx(0)->getTotalNumHs() == 3, "");
477 
478   m2.addAtom(new Atom(1), true, true);
479   m2.addBond(2, 3, Bond::SINGLE);
480   MolOps::sanitizeMol(m2);
481 
482   CHECK_INVARIANT(m2.getAtomWithIdx(0)->getTotalNumHs() == 3, "");
483   CHECK_INVARIANT(m2.getAtomWithIdx(0)->getTotalNumHs(true) == 3, "");
484   CHECK_INVARIANT(m2.getAtomWithIdx(2)->getTotalNumHs() == 2, "");
485   CHECK_INVARIANT(m2.getAtomWithIdx(2)->getTotalNumHs(true) == 3, "");
486 
487   Atom *other = m2.getBondWithIdx(1)->getOtherAtom(m2.getAtomWithIdx(1));
488   CHECK_INVARIANT(other, "");
489 
490   const Atom *at = m2.getAtomWithIdx(1);
491   ROMol::OEDGE_ITER begin, end;
492   boost::tie(begin, end) = m2.getAtomBonds(at);
493   while (begin != end) {
494     const Atom *at2 = m2[*begin]->getOtherAtom(at);
495     TEST_ASSERT(at2);
496     begin++;
497   }
498 
499   ROMol::VERTEX_ITER atBegin, atEnd;
500   boost::tie(atBegin, atEnd) = m2.getVertices();
501   TEST_ASSERT(atBegin != atEnd);
502   while (atBegin != atEnd) {
503     const Atom *at2 = m2[*atBegin];
504     TEST_ASSERT(at2->getIdx() == *atBegin);
505     atBegin++;
506   }
507 
508   BOOST_LOG(rdInfoLog) << "Finished" << std::endl;
509 }
510 
testDegree()511 void testDegree() {
512   BOOST_LOG(rdInfoLog) << "-----------------------\n Testing degree operations"
513                        << std::endl;
514   RWMol *m;
515 
516   m = new RWMol();
517   m->addAtom(new Atom(6), true, true);
518   m->addAtom(new Atom(6), true, true);
519   m->addAtom(new Atom(6), true, true);
520   m->addAtom(new Atom(6), true, true);
521   m->addBond(0, 1, Bond::SINGLE);
522   m->addBond(1, 2, Bond::SINGLE);
523   m->addBond(0, 2, Bond::SINGLE);
524   m->addBond(2, 3, Bond::SINGLE);
525 
526   MolOps::sanitizeMol(*m);
527   TEST_ASSERT(m->getAtomWithIdx(0)->getDegree() == 2);
528   TEST_ASSERT(m->getAtomWithIdx(0)->getTotalNumHs() == 2);
529   TEST_ASSERT(m->getAtomWithIdx(0)->getTotalDegree() == 4);
530   TEST_ASSERT(m->getAtomWithIdx(2)->getDegree() == 3);
531   TEST_ASSERT(m->getAtomWithIdx(2)->getTotalNumHs() == 1);
532   TEST_ASSERT(m->getAtomWithIdx(2)->getTotalDegree() == 4);
533 
534   delete m;
535   m = new RWMol();
536   m->addAtom(new Atom(6), true, true);
537   m->addAtom(new Atom(6), true, true);
538   m->addAtom(new Atom(6), true, true);
539   m->addAtom(new Atom(6), true, true);
540   m->addAtom(new Atom(1), true, true);
541   m->addBond(0, 1, Bond::SINGLE);
542   m->addBond(1, 2, Bond::SINGLE);
543   m->addBond(0, 2, Bond::SINGLE);
544   m->addBond(2, 3, Bond::SINGLE);
545   m->addBond(0, 4, Bond::SINGLE);
546 
547   MolOps::sanitizeMol(*m);
548   TEST_ASSERT(m->getAtomWithIdx(0)->getDegree() == 3);
549   TEST_ASSERT(m->getAtomWithIdx(0)->getTotalNumHs() == 1);
550   TEST_ASSERT(m->getAtomWithIdx(0)->getTotalNumHs(true) == 2);
551   TEST_ASSERT(m->getAtomWithIdx(0)->getTotalDegree() == 4);
552   TEST_ASSERT(m->getAtomWithIdx(2)->getDegree() == 3);
553   TEST_ASSERT(m->getAtomWithIdx(2)->getTotalNumHs() == 1);
554   TEST_ASSERT(m->getAtomWithIdx(2)->getTotalDegree() == 4);
555   delete m;
556 
557   BOOST_LOG(rdInfoLog) << "Finished" << std::endl;
558 }
559 
testIssue1993296()560 void testIssue1993296() {
561   auto *m = new RWMol();
562   bool ok;
563   BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl;
564   BOOST_LOG(rdInfoLog) << "Testing Issue 1993296" << std::endl;
565 
566   m->addAtom(new Atom(6), true, true);
567   m->addAtom(new Atom(6), true, true);
568   m->addBond(0, 1, Bond::SINGLE);
569   ok = false;
570   try {
571     m->addBond(0, 1, Bond::SINGLE);
572   } catch (...) {
573     ok = true;
574   }
575   TEST_ASSERT(ok);
576   ok = false;
577   try {
578     m->addBond(1, 0, Bond::SINGLE);
579   } catch (...) {
580     ok = true;
581   }
582   TEST_ASSERT(ok);
583 
584   // not technically part of 1993296, but related: we also throw
585   // on adding self bonds
586   ok = false;
587   try {
588     m->addBond(1, 1, Bond::SINGLE);
589   } catch (...) {
590     ok = true;
591   }
592 
593   auto *newB = new Bond();
594   newB->setBeginAtomIdx(0);
595   newB->setEndAtomIdx(1);
596   newB->setBondType(Bond::SINGLE);
597   ok = false;
598   try {
599     m->addBond(newB);
600   } catch (...) {
601     ok = true;
602   }
603   TEST_ASSERT(ok);
604 
605   // not technically part of 1993296, but related: we also throw
606   // on adding self bonds
607   newB->setBeginAtomIdx(0);
608   newB->setEndAtomIdx(0);
609   ok = false;
610   try {
611     m->addBond(newB);
612   } catch (...) {
613     ok = true;
614   }
615   TEST_ASSERT(ok);
616   delete newB;
617   delete m;
618 
619   BOOST_LOG(rdInfoLog) << "\tdone" << std::endl;
620 }
621 
testIssue2381580()622 void testIssue2381580() {
623   BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl;
624   BOOST_LOG(rdInfoLog) << "Testing Issue 2381580" << std::endl;
625 
626   {
627     auto *m = new RWMol();
628     m->addAtom(new Atom(5), true, true);
629     m->addAtom(new Atom(6), true, true);
630     m->addAtom(new Atom(6), true, true);
631     m->addAtom(new Atom(6), true, true);
632     m->addBond(0, 1, Bond::SINGLE);
633     m->addBond(0, 2, Bond::SINGLE);
634     m->addBond(0, 3, Bond::SINGLE);
635     MolOps::sanitizeMol(*m);
636     TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == 0);
637     TEST_ASSERT(m->getAtomWithIdx(0)->getExplicitValence() == 3);
638     TEST_ASSERT(m->getAtomWithIdx(0)->getNumImplicitHs() == 0);
639     delete m;
640   }
641 
642   {
643     auto *m = new RWMol();
644     m->addAtom(new Atom(5), true, true);
645     m->addAtom(new Atom(6), true, true);
646     m->addAtom(new Atom(6), true, true);
647     m->addAtom(new Atom(6), true, true);
648     m->addAtom(new Atom(6), true, true);
649     m->addBond(0, 1, Bond::SINGLE);
650     m->addBond(0, 2, Bond::SINGLE);
651     m->addBond(0, 3, Bond::SINGLE);
652     m->addBond(0, 4, Bond::SINGLE);
653     m->getAtomWithIdx(0)->setFormalCharge(-1);
654     MolOps::sanitizeMol(*m);
655     TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == -1);
656     TEST_ASSERT(m->getAtomWithIdx(0)->getExplicitValence() == 4);
657     TEST_ASSERT(m->getAtomWithIdx(0)->getNumImplicitHs() == 0);
658     delete m;
659   }
660 
661   {
662     auto *m = new RWMol();
663     m->addAtom(new Atom(5), true, true);
664     m->addAtom(new Atom(6), true, true);
665     m->addAtom(new Atom(6), true, true);
666     m->addAtom(new Atom(6), true, true);
667     m->addAtom(new Atom(6), true, true);
668     m->addBond(0, 1, Bond::SINGLE);
669     m->addBond(0, 2, Bond::SINGLE);
670     m->addBond(0, 3, Bond::SINGLE);
671     m->addBond(0, 4, Bond::SINGLE);
672     bool ok = false;
673     try {
674       MolOps::sanitizeMol(*m);
675     } catch (MolSanitizeException &) {
676       ok = true;
677     }
678     TEST_ASSERT(ok);
679     delete m;
680   }
681 
682   {
683     auto *m = new RWMol();
684     m->addAtom(new Atom(5), true, true);
685     m->addAtom(new Atom(6), true, true);
686     m->addAtom(new Atom(6), true, true);
687     m->addAtom(new Atom(6), true, true);
688     m->addAtom(new Atom(6), true, true);
689     m->addBond(0, 1, Bond::SINGLE);
690     m->addBond(0, 2, Bond::SINGLE);
691     m->addBond(0, 3, Bond::SINGLE);
692     m->addBond(0, 4, Bond::SINGLE);
693     m->getAtomWithIdx(0)->setFormalCharge(+1);
694     bool ok = false;
695     try {
696       MolOps::sanitizeMol(*m);
697     } catch (MolSanitizeException &) {
698       ok = true;
699     }
700     TEST_ASSERT(ok);
701     delete m;
702   }
703 
704   {
705     auto *m = new RWMol();
706     m->addAtom(new Atom(5), true, true);
707     m->addAtom(new Atom(6), true, true);
708     m->addAtom(new Atom(6), true, true);
709     m->addBond(0, 1, Bond::SINGLE);
710     m->addBond(0, 2, Bond::SINGLE);
711     m->getAtomWithIdx(0)->setFormalCharge(+1);
712     MolOps::sanitizeMol(*m);
713     TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == 1);
714     TEST_ASSERT(m->getAtomWithIdx(0)->getExplicitValence() == 2);
715     TEST_ASSERT(m->getAtomWithIdx(0)->getNumImplicitHs() == 0);
716     delete m;
717   }
718 
719   {
720     auto *m = new RWMol();
721     m->addAtom(new Atom(5), true, true);
722     m->addAtom(new Atom(6), true, true);
723     m->addAtom(new Atom(6), true, true);
724     m->addAtom(new Atom(6), true, true);
725     m->addBond(0, 1, Bond::SINGLE);
726     m->addBond(0, 2, Bond::SINGLE);
727     m->addBond(0, 3, Bond::SINGLE);
728     m->getAtomWithIdx(0)->setFormalCharge(-1);
729     MolOps::sanitizeMol(*m);
730     TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == -1);
731     TEST_ASSERT(m->getAtomWithIdx(0)->getExplicitValence() == 3);
732     TEST_ASSERT(m->getAtomWithIdx(0)->getNumImplicitHs() == 1);
733     TEST_ASSERT(m->getAtomWithIdx(0)->getExplicitValence() +
734                     m->getAtomWithIdx(0)->getImplicitValence() ==
735                 rdcast<int>(m->getAtomWithIdx(0)->getTotalValence()));
736     TEST_ASSERT(m->getAtomWithIdx(1)->getExplicitValence() +
737                     m->getAtomWithIdx(1)->getImplicitValence() ==
738                 rdcast<int>(m->getAtomWithIdx(1)->getTotalValence()));
739     TEST_ASSERT(m->getAtomWithIdx(2)->getExplicitValence() +
740                     m->getAtomWithIdx(2)->getImplicitValence() ==
741                 rdcast<int>(m->getAtomWithIdx(2)->getTotalValence()));
742     TEST_ASSERT(m->getAtomWithIdx(3)->getExplicitValence() +
743                     m->getAtomWithIdx(3)->getImplicitValence() ==
744                 rdcast<int>(m->getAtomWithIdx(3)->getTotalValence()));
745     delete m;
746   }
747 
748   BOOST_LOG(rdInfoLog) << "\tdone" << std::endl;
749 }
750 
testIssue2840217()751 void testIssue2840217() {
752   BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl;
753   BOOST_LOG(rdInfoLog) << "Testing Issue 2840217" << std::endl;
754 
755   {
756     auto *m = new RWMol();
757     for (unsigned int i = 0; i < 200; ++i) {
758       m->addAtom(new Atom(6), true, true);
759       m->addAtom(new Atom(6), true, true);
760       m->addAtom(new Atom(6), true, true);
761       m->addAtom(new Atom(6), true, true);
762       m->addAtom(new Atom(6), true, true);
763       m->addAtom(new Atom(6), true, true);
764       for (unsigned int j = 0; j < 5; ++j) {
765         m->addBond(i * 6 + j, i * 6 + j + 1, Bond::AROMATIC);
766       }
767       m->addBond(i * 6, i * 6 + 5, Bond::AROMATIC);
768     }
769     TEST_ASSERT(m->getNumBonds() == 1200);
770     MolOps::sanitizeMol(*m);
771     TEST_ASSERT(m->getNumAtoms() == 1200);
772     delete m;
773   }
774 
775   BOOST_LOG(rdInfoLog) << "\tdone" << std::endl;
776 }
777 
test1()778 void test1() {
779   {
780     RWMol m;
781     auto *newAtom = new Atom(8);
782 
783     m.addAtom(newAtom, true, true);
784     CHECK_INVARIANT(m.getAtomWithIdx(0)->getIdx() == 0, "");
785     newAtom = new Atom(6);
786     m.addAtom(newAtom, true, true);
787     CHECK_INVARIANT(m.getAtomWithIdx(0)->getIdx() == 0, "");
788     CHECK_INVARIANT(m.getAtomWithIdx(1)->getIdx() == 1, "");
789 
790     newAtom = new Atom(7);
791     m.addAtom(newAtom, true, true);
792     CHECK_INVARIANT(m.getAtomWithIdx(0)->getIdx() == 0, "");
793     CHECK_INVARIANT(m.getAtomWithIdx(1)->getIdx() == 1, "");
794     CHECK_INVARIANT(m.getAtomWithIdx(2)->getIdx() == 2, "");
795     CHECK_INVARIANT(
796         m.getAtomWithIdx(1)->getOwningMol().getAtomWithIdx(1)->getIdx() == 1,
797         "");
798 
799     m.addBond(0, 1, Bond::SINGLE);
800     m.addBond(1, 2, Bond::DOUBLE);
801     CHECK_INVARIANT(m.getBondWithIdx(0)->getIdx() == 0, "");
802     CHECK_INVARIANT(m.getBondWithIdx(1)->getIdx() == 1, "");
803 
804     CHECK_INVARIANT(m.getBondWithIdx(0)->getBondType() == Bond::SINGLE, "");
805     CHECK_INVARIANT(m.getBondWithIdx(1)->getBondType() == Bond::DOUBLE, "");
806 
807     CHECK_INVARIANT(m.getBondWithIdx(0)->getBeginAtom()->getIdx() == 0, "");
808     CHECK_INVARIANT(m.getBondWithIdx(0)->getEndAtom()->getIdx() == 1, "");
809     CHECK_INVARIANT(m.getBondWithIdx(1)->getBeginAtom()->getIdx() == 1, "");
810     CHECK_INVARIANT(m.getBondWithIdx(1)->getEndAtom()->getIdx() == 2, "");
811 
812     testBookmarks(m);
813 
814     // Using operator<< on a non-sanitized molecule is a test of Issue156:
815     ROMol::ADJ_ITER ai1, ai2;
816     boost::tie(ai1, ai2) = m.getAtomNeighbors(m.getAtomWithIdx(1));
817     m.updatePropertyCache();
818     boost::logging::disable_logs("rdApp.info");
819     while (ai1 != ai2) {
820       BOOST_LOG(rdInfoLog) << *m.getAtomWithIdx(*ai1) << endl;
821       ai1++;
822     }
823 
824     m.addAtom(new Atom(6), true, true);
825     Bond *bsp = m.createPartialBond(2);
826     m.setBondBookmark(bsp, 47);
827     m.finishPartialBond(3, 47, Bond::SINGLE);
828     m.clearBondBookmark(47);
829     BOOST_LOG(rdInfoLog) << "partial bond added:" << endl;
830     unsigned int i;
831     m.updatePropertyCache();
832     for (i = 0; i < m.getNumAtoms(); i++) {
833       Atom *a = m.getAtomWithIdx(i);
834       BOOST_LOG(rdInfoLog) << "\t" << *a << endl;
835     }
836 
837     int newAtNum = m.addAtom(new Atom(6), true, true);
838     m.addBond(0, newAtNum, Bond::SINGLE);
839 
840     BOOST_LOG(rdInfoLog) << "Again:" << endl;
841     m.updatePropertyCache();
842     for (i = 0; i < m.getNumAtoms(); i++) {
843       Atom *a = m.getAtomWithIdx(i);
844       BOOST_LOG(rdInfoLog) << "\t" << *a << endl;
845     }
846 
847     RWMol m2;
848     m2.addAtom(new Atom(6), true, true);
849     m2.addAtom(new Atom(6), true, true);
850     // QueryAtom *qA = new QueryAtom;
851     // qA->setAtomicNum(7);
852     // m2.addAtom(qA);
853     m2.addAtom(new QueryAtom(7), true, true);
854     m2.addBond(0, 1, Bond::TRIPLE);
855     m2.addBond(1, 2, Bond::SINGLE);
856 
857     m.insertMol(m2);
858     m.updatePropertyCache();
859     BOOST_LOG(rdInfoLog) << "post-insert:" << endl;
860     for (i = 0; i < m.getNumAtoms(); i++) {
861       Atom *a = m.getAtomWithIdx(i);
862       BOOST_LOG(rdInfoLog) << "\t" << *a << endl;
863     }
864 
865     BOOST_LOG(rdInfoLog) << " ------------------- " << endl;
866     auto *newA = new Atom(12);
867     int newIdx = m.addAtom(newA, true, true);
868     m.addBond(newIdx - 1, newIdx, Bond::AROMATIC);
869     // m.debugMol(cout);
870     BOOST_LOG(rdInfoLog) << " trying a replace " << endl;
871     auto *repA = new Atom(22);
872     m.replaceAtom(newIdx, repA);
873     delete repA;
874     TEST_ASSERT(m.getAtomWithIdx(newIdx)->getAtomicNum() == 22);
875     auto *nbnd = new Bond(Bond::DOUBLE);
876     TEST_ASSERT(m.getBondWithIdx(m.getNumBonds() - 1)->getBondType() ==
877                 Bond::AROMATIC);
878     m.replaceBond(m.getNumBonds() - 1, nbnd);
879     m.debugMol(std::cerr);
880     TEST_ASSERT(m.getBondWithIdx(m.getNumBonds() - 1)->getBondType() ==
881                 nbnd->getBondType());
882     delete nbnd;
883     delete bsp;
884   }
885   {
886     RWMol m;
887     m.addAtom(new Atom(6), true, true);
888     m.addAtom(new Atom(6), true, true);
889     m.addBond(0, 1, Bond::SINGLE);
890     auto *conf = new Conformer(m.getNumAtoms());
891     m.addConformer(conf);
892     m.getConformer().setAtomPos(0, RDGeom::Point3D(1.0, 0.0, 0.0));
893     m.getConformer().setAtomPos(1, RDGeom::Point3D(0.0, 1.0, 0.0));
894 
895     RWMol m2;
896     // insert molecule without a conf:
897     m2.addAtom(new Atom(6), true, true);
898     m.insertMol(m2);
899     TEST_ASSERT(m.getConformer().getNumAtoms() == m.getNumAtoms());
900     TEST_ASSERT(feq(m.getConformer().getAtomPos(2).x, 0.0));
901     TEST_ASSERT(feq(m.getConformer().getAtomPos(2).y, 0.0));
902     TEST_ASSERT(feq(m.getConformer().getAtomPos(2).z, 0.0));
903 
904     // insert molecule with a conf:
905     conf = new Conformer(m2.getNumAtoms());
906     m2.addConformer(conf);
907     m2.getConformer().setAtomPos(0, RDGeom::Point3D(1.0, 1.0, 0.0));
908     m.insertMol(m2);
909     TEST_ASSERT(m.getConformer().getNumAtoms() == m.getNumAtoms());
910     TEST_ASSERT(feq(m.getConformer().getAtomPos(2).x, 0.0));
911     TEST_ASSERT(feq(m.getConformer().getAtomPos(2).y, 0.0));
912     TEST_ASSERT(feq(m.getConformer().getAtomPos(2).z, 0.0));
913     TEST_ASSERT(feq(m.getConformer().getAtomPos(3).x, 1.0));
914     TEST_ASSERT(feq(m.getConformer().getAtomPos(3).y, 1.0));
915     TEST_ASSERT(feq(m.getConformer().getAtomPos(3).z, 0.0));
916   }
917   {
918     // start with a molecule with no conf
919     RWMol m;
920     m.addAtom(new Atom(6), true, true);
921     m.addAtom(new Atom(6), true, true);
922     m.addBond(0, 1, Bond::SINGLE);
923     TEST_ASSERT(m.getNumConformers() == 0);
924 
925     RWMol m2;
926     // insert molecule without a conf:
927     m2.addAtom(new Atom(6), true, true);
928     m.insertMol(m2);
929     TEST_ASSERT(m.getNumConformers() == 0);
930 
931     // insert molecule with a conf:
932     auto *conf = new Conformer(m2.getNumAtoms());
933     m2.addConformer(conf);
934     m2.getConformer().setAtomPos(0, RDGeom::Point3D(1.0, 1.0, 0.0));
935     m.insertMol(m2);
936     TEST_ASSERT(m.getNumConformers() == 1);
937     TEST_ASSERT(m.getConformer().getNumAtoms() == m.getNumAtoms());
938     TEST_ASSERT(feq(m.getConformer().getAtomPos(0).x, 0.0));
939     TEST_ASSERT(feq(m.getConformer().getAtomPos(0).y, 0.0));
940     TEST_ASSERT(feq(m.getConformer().getAtomPos(0).z, 0.0));
941     TEST_ASSERT(feq(m.getConformer().getAtomPos(3).x, 1.0));
942     TEST_ASSERT(feq(m.getConformer().getAtomPos(3).y, 1.0));
943     TEST_ASSERT(feq(m.getConformer().getAtomPos(3).z, 0.0));
944   }
945 }
946 
testPeriodicTable()947 void testPeriodicTable() {
948   BOOST_LOG(rdInfoLog) << "-----------------------\n";
949   BOOST_LOG(rdInfoLog) << "Testing properties from periodic table" << std::endl;
950 
951   TEST_ASSERT(PeriodicTable::getTable()->getDefaultValence(6) == 4);
952   TEST_ASSERT(PeriodicTable::getTable()->getNouterElecs(6) == 4);
953   TEST_ASSERT(PeriodicTable::getTable()->getMostCommonIsotope(6) == 12);
954   TEST_ASSERT(PeriodicTable::getTable()->getMostCommonIsotopeMass(6) == 12.0);
955   TEST_ASSERT(PeriodicTable::getTable()->getMostCommonIsotopeMass(6) == 12.0);
956   TEST_ASSERT(feq(PeriodicTable::getTable()->getMostCommonIsotopeMass(4),
957                   9.0122, 1e-4));
958   TEST_ASSERT(feq(PeriodicTable::getTable()->getRb0(6), 0.77, 1e-2));
959 
960   TEST_ASSERT(PeriodicTable::getTable()->getDefaultValence(26) == -1);
961   TEST_ASSERT(PeriodicTable::getTable()->getDefaultValence(57) == -1);
962 
963   // this was sf.net issue 269
964   int anum;
965   anum = PeriodicTable::getTable()->getAtomicNumber("C");
966   TEST_ASSERT(anum == 6);
967   try {
968     anum = PeriodicTable::getTable()->getAtomicNumber("Xx");
969   } catch (...) {
970     anum = -1;
971   }
972   TEST_ASSERT(anum == -1);
973 
974   BOOST_LOG(rdInfoLog) << "Finished" << std::endl;
975 }
976 
testAddAtomWithConf()977 void testAddAtomWithConf() {
978   BOOST_LOG(rdInfoLog) << "-----------------------\n";
979   BOOST_LOG(rdInfoLog) << "Testing issue 264: adding atoms to molecules that "
980                           "already have conformers"
981                        << std::endl;
982   {
983     RWMol m;
984 
985     m.addAtom(new Atom(6), true, true);
986     m.addAtom(new Atom(6), true, true);
987 
988     auto *conf = new Conformer(m.getNumAtoms());
989     m.addConformer(conf);
990 
991     m.addAtom(new Atom(6), true, true);
992     TEST_ASSERT(m.getConformer().getNumAtoms() == m.getNumAtoms());
993   }
994   {
995     RWMol m;
996 
997     m.addAtom(new Atom(6), true, true);
998     m.addAtom(new Atom(6), true, true);
999 
1000     auto *conf = new Conformer(m.getNumAtoms());
1001     m.addConformer(conf);
1002 
1003     m.addAtom();
1004     TEST_ASSERT(m.getConformer().getNumAtoms() == m.getNumAtoms());
1005   }
1006   {  // make sure things are ok even if there is no conformer
1007     RWMol m;
1008 
1009     m.addAtom(new Atom(6), true, true);
1010     m.addAtom(new Atom(6), true, true);
1011     m.addAtom(new Atom(6), true, true);
1012     TEST_ASSERT(m.getNumConformers() == 0);
1013   }
1014 
1015   BOOST_LOG(rdInfoLog) << "Finished" << std::endl;
1016 }
1017 
testIssue267()1018 void testIssue267() {
1019   BOOST_LOG(rdInfoLog) << "-----------------------\n";
1020   BOOST_LOG(rdInfoLog) << "Testing issue 267: default valence of *"
1021                        << std::endl;
1022   {
1023     RWMol m;
1024 
1025     m.addAtom(new Atom(0), true, true);
1026     m.updatePropertyCache();
1027 
1028     TEST_ASSERT(m.getAtomWithIdx(0)->getImplicitValence() == 0);
1029   }
1030   {
1031     RWMol m;
1032 
1033     m.addAtom(new Atom(0), true, true);
1034     for (unsigned int i = 0; i < 8; ++i) {
1035       m.addAtom(new Atom(1), true, true);
1036       m.addBond(0, i + 1, Bond::SINGLE);
1037     }
1038     m.updatePropertyCache();
1039   }
1040   BOOST_LOG(rdInfoLog) << "Finished" << std::endl;
1041 }
1042 
testIssue284()1043 void testIssue284() {
1044   BOOST_LOG(rdInfoLog) << "-----------------------\n";
1045   BOOST_LOG(rdInfoLog) << "Testing issue 284: removeBond not updating indices"
1046                        << std::endl;
1047   {
1048     RWMol m;
1049 
1050     m.addAtom(new Atom(6), true, true);
1051     m.addAtom(new Atom(6), true, true);
1052     m.addAtom(new Atom(6), true, true);
1053     m.addBond(0, 1, Bond::SINGLE);
1054     m.addBond(1, 2, Bond::SINGLE);
1055     m.updatePropertyCache();
1056     TEST_ASSERT(m.getBondBetweenAtoms(0, 1)->getIdx() == 0);
1057     TEST_ASSERT(m.getBondBetweenAtoms(1, 2)->getIdx() == 1);
1058     m.removeBond(0, 1);
1059     TEST_ASSERT(!m.getBondBetweenAtoms(0, 1));
1060     TEST_ASSERT(m.getBondBetweenAtoms(1, 2)->getIdx() == 0);
1061   }
1062   BOOST_LOG(rdInfoLog) << "Finished" << std::endl;
1063 }
1064 
testAtomResidues()1065 void testAtomResidues() {
1066   BOOST_LOG(rdInfoLog) << "-----------------------\n";
1067   BOOST_LOG(rdInfoLog) << "Testing residue information handling on atoms"
1068                        << std::endl;
1069   {
1070     auto *m = new RWMol();
1071 
1072     m->addAtom(new Atom(6), true, true);
1073     m->addAtom(new Atom(6), true, true);
1074     m->addBond(0, 1, Bond::SINGLE);
1075     m->addAtom(new Atom(6), true, true);
1076     m->addBond(1, 2, Bond::SINGLE);
1077     m->addAtom(new Atom(6), true, true);
1078     m->addBond(2, 3, Bond::SINGLE);
1079 
1080     TEST_ASSERT(!(m->getAtomWithIdx(0)->getMonomerInfo()));
1081     TEST_ASSERT(!(m->getAtomWithIdx(1)->getMonomerInfo()));
1082     TEST_ASSERT(!(m->getAtomWithIdx(2)->getMonomerInfo()));
1083     TEST_ASSERT(!(m->getAtomWithIdx(3)->getMonomerInfo()));
1084 
1085     m->getAtomWithIdx(0)->setMonomerInfo(
1086         new AtomMonomerInfo(AtomMonomerInfo::OTHER, "m1"));
1087     TEST_ASSERT((m->getAtomWithIdx(0)->getMonomerInfo()));
1088     TEST_ASSERT(m->getAtomWithIdx(0)->getMonomerInfo()->getName() == "m1");
1089 
1090     m->getAtomWithIdx(1)->setMonomerInfo(new AtomPDBResidueInfo("Ca", 3));
1091     TEST_ASSERT((m->getAtomWithIdx(1)->getMonomerInfo()));
1092     TEST_ASSERT(m->getAtomWithIdx(1)->getMonomerInfo()->getName() == "Ca");
1093     TEST_ASSERT(static_cast<const AtomPDBResidueInfo *>(
1094                     m->getAtomWithIdx(1)->getMonomerInfo())
1095                     ->getSerialNumber() == 3);
1096 
1097     auto *m2 = new RWMol(*m);
1098     delete m;
1099 
1100     TEST_ASSERT((m2->getAtomWithIdx(0)->getMonomerInfo()));
1101     TEST_ASSERT(m2->getAtomWithIdx(0)->getMonomerInfo()->getName() == "m1");
1102     TEST_ASSERT((m2->getAtomWithIdx(1)->getMonomerInfo()));
1103     TEST_ASSERT(m2->getAtomWithIdx(1)->getMonomerInfo()->getName() == "Ca");
1104     TEST_ASSERT(static_cast<const AtomPDBResidueInfo *>(
1105                     m2->getAtomWithIdx(1)->getMonomerInfo())
1106                     ->getSerialNumber() == 3);
1107     TEST_ASSERT(!(m2->getAtomWithIdx(2)->getMonomerInfo()));
1108     TEST_ASSERT(!(m2->getAtomWithIdx(3)->getMonomerInfo()));
1109     delete m2;
1110   }
1111   BOOST_LOG(rdInfoLog) << "Finished" << std::endl;
1112 }
1113 
testNeedsUpdatePropertyCache()1114 void testNeedsUpdatePropertyCache() {
1115   BOOST_LOG(rdInfoLog) << "-----------------------\n";
1116   BOOST_LOG(rdInfoLog) << "Testing function needsUpdatePropertyCache"
1117                        << std::endl;
1118   {
1119     RWMol m;
1120 
1121     m.addAtom(new Atom(0), true, true);
1122     TEST_ASSERT(m.needsUpdatePropertyCache() == true);
1123     m.updatePropertyCache();
1124 
1125     TEST_ASSERT(m.getAtomWithIdx(0)->getImplicitValence() == 0);
1126     TEST_ASSERT(m.needsUpdatePropertyCache() == false);
1127   }
1128   {
1129     RWMol m;
1130 
1131     m.addAtom(new Atom(6), true, true);
1132     for (ROMol::AtomIterator atomIt = m.beginAtoms(); atomIt != m.endAtoms();
1133          ++atomIt) {
1134       (*atomIt)->calcExplicitValence(false);
1135       (*atomIt)->calcImplicitValence(false);
1136     }
1137     m.addAtom(new Atom(6), true, true);
1138     m.addBond(0, 1, Bond::SINGLE);
1139     TEST_ASSERT(m.needsUpdatePropertyCache() == true);
1140     m.updatePropertyCache();
1141     TEST_ASSERT(m.needsUpdatePropertyCache() == false);
1142   }
1143   {
1144     RWMol m;
1145     m.addAtom(new Atom(6), true, true);
1146     m.getAtomWithIdx(0)->calcExplicitValence(false);
1147     TEST_ASSERT(m.getAtomWithIdx(0)->needsUpdatePropertyCache());
1148     m.getAtomWithIdx(0)->setNoImplicit(true);
1149     TEST_ASSERT(!m.getAtomWithIdx(0)->needsUpdatePropertyCache());
1150   }
1151   BOOST_LOG(rdInfoLog) << "Finished" << std::endl;
1152 }
1153 
1154 namespace {
qhelper(Atom::QUERYATOM_QUERY * q,unsigned int depth=0)1155 std::string qhelper(Atom::QUERYATOM_QUERY *q, unsigned int depth = 0) {
1156   std::string res = "";
1157   if (q) {
1158     for (unsigned int i = 0; i < depth; ++i) {
1159       res += "  ";
1160     }
1161     res += q->getFullDescription() + "\n";
1162     for (auto ci = q->beginChildren(); ci != q->endChildren(); ++ci) {
1163       res += qhelper((*ci).get(), depth + 1);
1164     }
1165   }
1166   return res;
1167 }
1168 }  // namespace
1169 
1170 const char *m_als_mol =
1171     "\n"
1172     "  Marvin  08200814552D          \n"
1173     "\n"
1174     "  9  8  0  0  0  0            999 V2000\n"
1175     "   -1.9152    1.6205    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0\n"
1176     "   -1.0902    1.6205    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0\n"
1177     "   -0.5068    2.2039    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0\n"
1178     "   -2.3277    0.9061    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0\n"
1179     "   -2.3277    2.3350    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0\n"
1180     "   -3.1527    2.3350    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0\n"
1181     "   -3.6830    2.8727    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0\n"
1182     "   -3.1527    0.9061    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0\n"
1183     "   -3.6771    0.2814    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0\n"
1184     "  1  2  2  0  0  0  0\n"
1185     "  2  3  1  0  0  0  0\n"
1186     "  1  4  1  0  0  0  0\n"
1187     "  1  5  1  0  0  0  0\n"
1188     "  5  6  2  0  0  0  0\n"
1189     "  6  7  1  0  0  0  0\n"
1190     "  4  8  2  0  0  0  0\n"
1191     "  8  9  1  0  0  0  0\n"
1192     "M  ALS   4  2 F O   Cl  \n"
1193     "M  END\n";
1194 
testAtomListLineRoundTrip()1195 void testAtomListLineRoundTrip() {
1196   BOOST_LOG(rdInfoLog) << "-----------------------\n";
1197   BOOST_LOG(rdInfoLog) << "Test AtomListLine RoundTrip" << std::endl;
1198   std::string rdbase = getenv("RDBASE");
1199   std::string fName = rdbase + "/Code/GraphMol/test_data/m_als_round_trip.mol";
1200   const bool sanitize = false;
1201   const bool removeHs = true;
1202   const bool strictParsing = true;
1203   unsigned int line = 0;
1204 
1205   std::istringstream inStream(m_als_mol);
1206   RWMol *m =
1207       MolDataStreamToMol(inStream, line, sanitize, removeHs, strictParsing);
1208   std::string desc = qhelper(m->getAtomWithIdx(3)->getQuery());
1209   TEST_ASSERT(m);
1210   TEST_ASSERT(m->getNumAtoms() == 9);
1211 
1212   std::string molblock = MolToMolBlock(*m);
1213   TEST_ASSERT(molblock.find(" ALS ") != std::string::npos);
1214   std::istringstream inStream2(molblock);
1215   RWMol *m2 =
1216       MolDataStreamToMol(inStream2, line, sanitize, removeHs, strictParsing);
1217   TEST_ASSERT(m2);
1218   TEST_ASSERT(desc == qhelper(m2->getAtomWithIdx(3)->getQuery()));
1219   Atom *cl(new Atom(17));
1220   Atom *o(new Atom(8));
1221   TEST_ASSERT(dynamic_cast<QueryAtom *>(m->getAtomWithIdx(3))->Match(cl));
1222   TEST_ASSERT(dynamic_cast<QueryAtom *>(m->getAtomWithIdx(3))->Match(o));
1223   TEST_ASSERT(dynamic_cast<QueryAtom *>(m2->getAtomWithIdx(3))->Match(cl));
1224   TEST_ASSERT(dynamic_cast<QueryAtom *>(m2->getAtomWithIdx(3))->Match(o));
1225   delete cl;
1226   delete o;
1227   delete m;
1228   delete m2;
1229   BOOST_LOG(rdInfoLog) << "Finished" << std::endl;
1230 }
1231 
testAtomListLineWithOtherQueries()1232 void testAtomListLineWithOtherQueries() {
1233   BOOST_LOG(rdInfoLog) << "-----------------------\n";
1234   BOOST_LOG(rdInfoLog) << "Test AtomListLine with other queries" << std::endl;
1235   const std::list<std::string> molblocks{R"MOL(
1236   SciTegic06022014352D
1237 
1238   4  3  0  0  0  0            999 V2000
1239    14.8926   -7.5485    0.0000 N   0  3  0  0  0  0  0  0  0  1  0  0
1240    13.8697   -6.9579    0.0000 L   0  0  0  0  0  0  0  0  0  4  0  0
1241    15.9154   -6.9579    0.0000 A   0  0  0  0  0  0  0  0  0  3  0  0
1242    14.8926   -8.7296    0.0000 L   0  5  0  0  0  0  0  0  0  2  0  0
1243   2  1  5  0  0  0  2
1244   1  3  1  0  0  0  2
1245   4  1  1  0  0  0  8
1246 M  CHG  2   1   1   4  -1
1247 M  SUB  1   4   1
1248 M  ALS   2  2 F O   S
1249 M  ALS   4  2 F O   S
1250 M  END
1251 )MOL",
1252                                          R"MOL(
1253   SciTegic06022014352D
1254 
1255   4  3  0  0  0  0            999 V2000
1256    14.8926   -7.5485    0.0000 N   0  3  0  0  0  0  0  0  0  1  0  0
1257    13.8697   -6.9579    0.0000 L   0  0  0  0  0  0  0  0  0  4  0  0
1258    15.9154   -6.9579    0.0000 A   0  0  0  0  0  0  0  0  0  3  0  0
1259    14.8926   -8.7296    0.0000 L   0  5  0  0  0  0  0  0  0  2  0  0
1260   2  1  5  0  0  0  2
1261   1  3  1  0  0  0  2
1262   4  1  1  0  0  0  8
1263 M  CHG  2   1   1   4  -1
1264 M  ALS   2  2 F O   S
1265 M  ALS   4  2 F O   S
1266 M  SUB  1   4   1
1267 M  END
1268   )MOL"};
1269   const bool sanitize = false;
1270   const bool removeHs = true;
1271   const bool strictParsing = true;
1272 
1273   for (const auto &molblock : molblocks) {
1274     RWMOL_SPTR m(MolBlockToMol(molblock, sanitize, removeHs, strictParsing));
1275     TEST_ASSERT(m->getNumAtoms() == 4);
1276     const Atom *a = m->getAtomWithIdx(3);
1277     TEST_ASSERT(a->hasQuery());
1278     const QueryAtom *qa = dynamic_cast<const QueryAtom *>(a);
1279     TEST_ASSERT(qa);
1280     TEST_ASSERT(describeQuery(a) == R"MOL(AtomAnd
1281   AtomAnd
1282     AtomOr
1283       AtomAtomicNum 8 = val
1284       AtomAtomicNum 16 = val
1285     AtomFormalCharge -1 = val
1286   AtomExplicitDegree 1 = val
1287 )MOL");
1288     TEST_ASSERT(SmartsWrite::GetAtomSmarts(qa) == "[#8,#16;-;D1:2]");
1289   }
1290 }
1291 
testReplaceChargedAtomWithQueryAtom()1292 void testReplaceChargedAtomWithQueryAtom() {
1293   BOOST_LOG(rdInfoLog) << "-----------------------\n";
1294   BOOST_LOG(rdInfoLog) << "Test replaceAtomWithQueryAtom on a charged atom"
1295                        << std::endl;
1296   auto mol = "[NH3+]C"_smiles;
1297   TEST_ASSERT(mol.get());
1298   auto a = mol->getAtomWithIdx(0);
1299   TEST_ASSERT(a);
1300   const QueryAtom *qa = dynamic_cast<const QueryAtom *>(
1301       QueryOps::replaceAtomWithQueryAtom(mol.get(), a));
1302   TEST_ASSERT(qa);
1303   TEST_ASSERT(SmartsWrite::GetAtomSmarts(qa) == "[#7&+]");
1304 }
1305 
testGithub608()1306 void testGithub608() {
1307   BOOST_LOG(rdInfoLog) << "-----------------------\n";
1308   BOOST_LOG(rdInfoLog) << "Test github 608: stereo bonds wrong after insertMol"
1309                        << std::endl;
1310 
1311   {
1312     RWMol *m = SmilesToMol("N1NN1");
1313     TEST_ASSERT(m);
1314     TEST_ASSERT(m->getNumAtoms() == 3);
1315     RWMol *f = SmilesToMol("C/C=C/C");
1316     TEST_ASSERT(f);
1317     TEST_ASSERT(f->getNumAtoms() == 4);
1318     TEST_ASSERT(f->getBondBetweenAtoms(1, 2)->getStereoAtoms().size() == 2);
1319     TEST_ASSERT(f->getBondBetweenAtoms(1, 2)->getStereoAtoms()[0] == 0);
1320     TEST_ASSERT(f->getBondBetweenAtoms(1, 2)->getStereoAtoms()[1] == 3);
1321 
1322     m->insertMol(*f);
1323     TEST_ASSERT(m->getNumAtoms() == 7);
1324     TEST_ASSERT(m->getBondBetweenAtoms(4, 5)->getBondType() == Bond::DOUBLE);
1325     TEST_ASSERT(m->getBondBetweenAtoms(4, 5)->getStereoAtoms().size() == 2);
1326     TEST_ASSERT(m->getBondBetweenAtoms(4, 5)->getStereoAtoms()[0] == 3);
1327     TEST_ASSERT(m->getBondBetweenAtoms(4, 5)->getStereoAtoms()[1] == 6);
1328 
1329     delete m;
1330     delete f;
1331   }
1332 
1333   {
1334     INT_VECT nAtoms;
1335     RWMol *m = SmilesToMol("N1NN1");
1336     TEST_ASSERT(m);
1337     TEST_ASSERT(m->getNumAtoms() == 3);
1338     RWMol *f = SmilesToMol("C[C@]1(F)CC[C@](Cl)(Br)CC1");
1339     TEST_ASSERT(f);
1340     TEST_ASSERT(f->getNumAtoms() == 10);
1341     TEST_ASSERT(f->getAtomWithIdx(1)->getPropIfPresent(
1342         common_properties::_ringStereoAtoms, nAtoms));
1343     TEST_ASSERT(std::find(nAtoms.begin(), nAtoms.end(), 6) != nAtoms.end());
1344     m->insertMol(*f);
1345     TEST_ASSERT(m->getNumAtoms() == 13);
1346     TEST_ASSERT(m->getAtomWithIdx(4)->getPropIfPresent(
1347         common_properties::_ringStereoAtoms, nAtoms));
1348     TEST_ASSERT(std::find(nAtoms.begin(), nAtoms.end(), 9) != nAtoms.end());
1349 
1350     delete m;
1351     delete f;
1352   }
1353   BOOST_LOG(rdInfoLog) << "Finished" << std::endl;
1354 }
1355 
1356 #ifdef RDK_TEST_MULTITHREADED
1357 #include <thread>
1358 #include <future>
1359 #include <RDGeneral/BoostStartInclude.h>
1360 #include <boost/dynamic_bitset.hpp>
1361 #include <RDGeneral/BoostEndInclude.h>
1362 namespace {
runblock(std::vector<const PeriodicTable * > * pts,int idx)1363 void runblock(std::vector<const PeriodicTable *> *pts, int idx) {
1364   const PeriodicTable *pt = PeriodicTable::getTable();
1365   (*pts)[idx] = pt;
1366   TEST_ASSERT(pt->getAtomicNumber("C") == 6);
1367   TEST_ASSERT(pt->getAtomicNumber("N") == 7);
1368   TEST_ASSERT(pt->getAtomicNumber("O") == 8);
1369 };
1370 }  // namespace
testGithub381()1371 void testGithub381() {
1372   BOOST_LOG(rdErrorLog) << "-------------------------------------" << std::endl;
1373   BOOST_LOG(rdErrorLog)
1374       << "    Test github381: thread-safe initialization of the periodic table"
1375       << std::endl;
1376 
1377   std::vector<std::future<void>> tg;
1378   unsigned int count = 32;
1379   std::vector<const PeriodicTable *> pts(count);
1380 #if 1
1381   for (unsigned int i = 0; i < count; ++i) {
1382     std::cerr.flush();
1383     tg.emplace_back(std::async(std::launch::async, runblock, &pts, i));
1384   }
1385   for (auto &fut : tg) {
1386     fut.get();
1387   }
1388   TEST_ASSERT(pts[0] != nullptr);
1389   for (unsigned int i = 1; i < count; ++i) {
1390     TEST_ASSERT(pts[i] == pts[0]);
1391   }
1392 #endif
1393 
1394   BOOST_LOG(rdErrorLog) << "  done" << std::endl;
1395 }
1396 #else
testGithub381()1397 void testGithub381() {}
1398 #endif
1399 
testGithub1041()1400 void testGithub1041() {
1401   BOOST_LOG(rdErrorLog) << "-------------------------------------" << std::endl;
1402   BOOST_LOG(rdErrorLog) << "    Test github1041: Segfault for atom with no "
1403                            "owner (expect some warnings)"
1404                         << std::endl;
1405   {
1406     Atom at(6);
1407     bool ok = false;
1408     try {
1409       at.getOwningMol();
1410     } catch (const Invar::Invariant &) {
1411       ok = true;
1412     }
1413     TEST_ASSERT(ok);
1414   }
1415   {
1416     Bond b;
1417     bool ok = false;
1418     try {
1419       b.getOwningMol();
1420     } catch (const Invar::Invariant &) {
1421       ok = true;
1422     }
1423     TEST_ASSERT(ok);
1424   }
1425   BOOST_LOG(rdErrorLog) << "  done" << std::endl;
1426 }
1427 
testGithub1453()1428 void testGithub1453() {
1429   BOOST_LOG(rdErrorLog) << "-------------------------------------" << std::endl;
1430   BOOST_LOG(rdErrorLog)
1431       << "    Test github1453: RWMol.clear() should reset the numBonds count."
1432       << std::endl;
1433 
1434   RWMol m2;
1435 
1436   m2.addAtom(new Atom(6), true, true);
1437   m2.addAtom(new Atom(6), true, true);
1438   m2.addBond(0, 1, Bond::TRIPLE);
1439   TEST_ASSERT(m2.getNumAtoms() == 2);
1440   TEST_ASSERT(m2.getNumBonds() == 1);
1441   m2.clear();
1442   TEST_ASSERT(m2.getNumAtoms() == 0);
1443   TEST_ASSERT(m2.getNumBonds() == 0);
1444 
1445   BOOST_LOG(rdErrorLog) << "  done" << std::endl;
1446 }
1447 
testRanges()1448 void testRanges() {
1449   BOOST_LOG(rdErrorLog) << "-------------------------------------" << std::endl;
1450   BOOST_LOG(rdErrorLog) << "    Test range-based for loops." << std::endl;
1451   RWMol *m = SmilesToMol("C1CC1");
1452   TEST_ASSERT(m);
1453   TEST_ASSERT(m->getNumAtoms() == 3);
1454 
1455   // For by value (ok since the atoms are pointers)
1456   unsigned int i = 0;
1457   for (auto atom : m->atoms()) {
1458     TEST_ASSERT(atom->getIdx() == i);
1459     i++;
1460   }
1461 
1462   // try refs
1463   i = 0;
1464   for (auto &atom : m->atoms()) {
1465     TEST_ASSERT(atom->getIdx() == i);
1466     i++;
1467   }
1468 
1469   // try const refs
1470   i = 0;
1471   for (auto const &atom : m->atoms()) {
1472     TEST_ASSERT(atom->getIdx() == i);
1473     i++;
1474   }
1475 
1476   const RWMol *cm = m;
1477   i = 0;
1478   for (auto atom : cm->atoms()) {
1479     TEST_ASSERT(atom->getIdx() == i);
1480     i++;
1481   }
1482 
1483   i = 0;
1484   for (auto bond : m->bonds()) {
1485     TEST_ASSERT(bond->getIdx() == i);
1486     i++;
1487   }
1488 
1489   i = 0;
1490   for (auto bond : cm->bonds()) {
1491     TEST_ASSERT(bond->getIdx() == i);
1492     i++;
1493   }
1494 
1495   const auto atom = m->getAtomWithIdx(0);
1496   i = 0;
1497   for (const auto &nbri :
1498        boost::make_iterator_range(m->getAtomNeighbors(atom))) {
1499     const auto &nbr = (*m)[nbri];
1500     TEST_ASSERT(nbr->getAtomicNum() == 6);
1501     i++;
1502   }
1503   TEST_ASSERT(i == 2);
1504 
1505   i = 0;
1506   for (const auto &nbri : boost::make_iterator_range(m->getAtomBonds(atom))) {
1507     const auto &bnd = (*m)[nbri];
1508     TEST_ASSERT(bnd->getBondType() == Bond::SINGLE);
1509     i++;
1510   }
1511   TEST_ASSERT(i == 2);
1512 
1513   // non-const versions of the same things
1514   i = 0;
1515   for (const auto &nbri :
1516        boost::make_iterator_range(m->getAtomNeighbors(atom))) {
1517     auto nbr = (*m)[nbri];
1518     TEST_ASSERT(nbr->getAtomicNum() == 6);
1519     nbr->setAtomicNum(7);
1520     nbr->setAtomicNum(6);
1521     i++;
1522   }
1523   TEST_ASSERT(i == 2);
1524 
1525   i = 0;
1526   for (const auto &nbri : boost::make_iterator_range(m->getAtomBonds(atom))) {
1527     auto bnd = (*m)[nbri];
1528     TEST_ASSERT(bnd->getBondType() == Bond::SINGLE);
1529     bnd->setBondType(Bond::DOUBLE);
1530     bnd->setBondType(Bond::SINGLE);
1531     i++;
1532   }
1533   TEST_ASSERT(i == 2);
1534 
1535   delete m;
1536 
1537   BOOST_LOG(rdErrorLog) << "  done" << std::endl;
1538 }
1539 
testGithub1642()1540 void testGithub1642() {
1541   BOOST_LOG(rdErrorLog) << "-------------------------------------" << std::endl;
1542   BOOST_LOG(rdErrorLog)
1543       << "    Test github1642: Atom index type is too low for big molecules."
1544       << std::endl;
1545   RWMol m2;
1546 
1547   unsigned int big_num_atoms = 70000;
1548   Atom *carbon = new Atom(6);
1549   for (unsigned int i = 0; i < big_num_atoms; ++i) {
1550     m2.addAtom(carbon);
1551   }
1552 
1553   TEST_ASSERT(m2.getNumAtoms() == big_num_atoms);
1554 
1555   for (int i = big_num_atoms; i > 0; --i) {
1556     m2.removeAtom(i - 1);
1557   }
1558   TEST_ASSERT(m2.getNumAtoms() == 0);
1559 
1560   delete carbon;
1561   BOOST_LOG(rdErrorLog) << "Finished" << std::endl;
1562 }
1563 
testGithub1843()1564 void testGithub1843() {
1565   BOOST_LOG(rdErrorLog) << "-------------------------------------" << std::endl;
1566   BOOST_LOG(rdErrorLog)
1567       << "    Test github1843: RWMol.clear() should not destroy ring pointer."
1568       << std::endl;
1569 
1570   RWMol *m = SmilesToMol("N1NN1");
1571   m->clear();
1572   MolOps::sanitizeMol(*m);
1573   delete m;
1574   BOOST_LOG(rdErrorLog) << "Finished" << std::endl;
1575 }
1576 
1577 // -------------------------------------------------------------------
main()1578 int main() {
1579   RDLog::InitLogs();
1580 // boost::logging::enable_logs("rdApp.info");
1581 #if 1
1582   test1();
1583   testPropLeak();
1584   testMolProps();
1585   testAtomProps();
1586   testBondProps();
1587   testMisc();
1588   testDegree();
1589   testIssue1993296();
1590   testIssue2381580();
1591   testIssue2840217();
1592   testPeriodicTable();
1593   testAddAtomWithConf();
1594   testIssue267();
1595   testIssue284();
1596   testClearMol();
1597   testAtomResidues();
1598   testNeedsUpdatePropertyCache();
1599   testGithub608();
1600   testGithub381();
1601   testGithub1041();
1602   testGithub1041();
1603   testGithub1453();
1604   testRanges();
1605   testGithub1642();
1606   testGithub1843();
1607 #endif
1608   testAtomListLineRoundTrip();
1609   testAtomListLineWithOtherQueries();
1610   testReplaceChargedAtomWithQueryAtom();
1611 
1612   return 0;
1613 }
1614