1 #include "obtest.h"
2 #include <openbabel/mol.h>
3 #include <openbabel/obconversion.h>
4 #include <openbabel/phmodel.h>
5 #include <openbabel/elements.h>
6 #include <openbabel/atom.h>
7 #include <openbabel/obiter.h>
8 #include <openbabel/bond.h>
9 #include <openbabel/generic.h>
10 #include <openbabel/forcefield.h>
11 
12 #include <iostream>
13 #include <string>
14 #include <vector>
15 #include <algorithm>
16 
17 using namespace std;
18 using namespace OpenBabel;
19 
test_Fix1912_PDBReading()20 void test_Fix1912_PDBReading()
21 {
22   // Reading from a PDB file should set the residues
23   // and mark chains as perceived
24   OBMolPtr mol = OBTestUtil::ReadFile("00T_ideal_het.pdb");
25   OB_ASSERT(mol->HasChainsPerceived());
26   OBAtom* atom = mol->GetAtom(1);
27   OBResidue* res = atom->GetResidue();
28   OB_REQUIRE(res != nullptr);
29   OB_COMPARE(res->GetAtomID(atom), " N19");
30   OB_COMPARE(res->GetChain(), 'A');
31 }
32 
remove_slashr(const char * smi)33 std::string remove_slashr(const char* smi)
34 {
35   // Remove \r if present to normalise across platforms
36   std::string ans;
37   const char *p = smi;
38   while (*p) {
39     if (*p != '\r')
40       ans += *p;
41     p++;
42   }
43   return ans;
44 }
45 
46 struct CdxData {
47   const char* fname;
48   const char* smi;
49 };
50 
51 // Some basic reading of ChemDraw files
52 // Note that we don't correctly read radicals - TODO
53 // Also, converting ChemDraw doesn't work with the Read() interface, only Convert()
test_ChemDraw_Basic()54 void test_ChemDraw_Basic()
55 {
56   static const CdxData cdxData[] = {
57     { "ethanol.cdx", "CCO\t\n" },
58     // cyclohexane -> benzene reaction, plus another cyclohexane drawn on its own
59     { "molrxnmix.cdx", "C1CCCCC1>>c1ccccc1\t\nC1CCCCC1\t\n" },
60     { "MeCN.cdx", "CC#N\t\n"}
61   };
62 
63   ios_base::openmode imode = ios_base::in | ios_base::binary;
64   unsigned int size = sizeof(cdxData) / sizeof(CdxData);
65   OBConversion conv;
66   OB_REQUIRE(conv.SetInAndOutFormats("cdx", "smi"));
67   std::stringstream outs;
68   conv.SetOutStream(&outs);
69 
70   for (int i=0; i<size; ++i) {
71     std::string fname = OBTestUtil::GetFilename(cdxData[i].fname);
72     std::ifstream ifs(fname.c_str(), imode);
73     OB_REQUIRE(ifs.good());
74     conv.SetInStream(&ifs);
75     outs.str("");
76     conv.Convert();
77     std::string out = outs.str();
78     OB_COMPARE(remove_slashr(out.c_str()), cdxData[i].smi);
79   }
80 }
81 
82 // A basic test of functionality
test_OBChemTsfm()83 void test_OBChemTsfm()
84 {
85   OBMol mol;
86   OBConversion conv;
87   conv.SetInFormat("smi");
88   conv.SetOutFormat("smi");
89 
90   // Notes to self: Problems with OBChemTsfm:
91   // tsfm.Init("Cl[C:1]-[C:2]", "[C:1]=[C:2]"); // TODO: Need to change the API to take const char
92   // Init should wipe the state so that OBChemTsfm can safely be reused
93 
94   conv.ReadString(&mol, "NCCBr");
95   OBChemTsfm tsfm;
96   std::string start("[N:1]-C-C");
97   std::string end("[N+:1]");
98   tsfm.Init(start, end);
99   tsfm.Apply(mol);
100   std::string out = conv.WriteString(&mol, true);
101   OB_COMPARE(out, "[NH3+]CCBr");
102 
103   conv.ReadString(&mol, "ClCCBr");
104   start = "Cl[C:1]-[C:2]";
105   end = "[C:1]=[C:2]";
106   OBChemTsfm b;
107   b.Init(start, end);
108   b.Apply(mol);
109   out = conv.WriteString(&mol, true);
110   OB_COMPARE(out, "ClC=CBr");
111 
112   conv.ReadString(&mol, "ClC(=O)[O]");
113   start = "[#6]-[OD1:1]";
114   end = "[#6]-[O-1:1]";
115   OBChemTsfm c;
116   c.Init(start, end);
117   c.Apply(mol);
118   out = conv.WriteString(&mol, true);
119   OB_COMPARE(out, "ClC(=O)[O-]");
120 
121   conv.ReadString(&mol, "Cl[C]CBr");
122   start = "Cl[C:1]-[C:2]";
123   end = "[C:1]=[C:2]";
124   OBChemTsfm d;
125   d.Init(start, end);
126   d.Apply(mol);
127   out = conv.WriteString(&mol, true);
128   OB_COMPARE(out, "Cl[C]=CBr");
129 }
130 
131 // Open Babel was previously disappearing triple bonds when provided with SMILES
132 // containing a triple bond in an aromatic ring
test_AromaticTripleBond()133 void test_AromaticTripleBond()
134 {
135   const char* smis[] = {
136     "CCc1n[c]#[c]n1CC2CC(C(=O)O2)(c3ccccc3)c4ccccc4",
137     "CCc1nc#cn1CC2CC(C(=O)O2)(c3ccccc3)c4ccccc4" };
138 
139   OBConversion conv;
140   conv.SetInFormat("smi");
141 
142   for(int i=0; i<2; ++i) {
143     OBMol mol;
144     conv.ReadString(&mol, smis[i]);
145     bool hasTripleBond = false;
146     FOR_BONDS_OF_MOL(bond, mol) {
147       if (bond->GetBondOrder()==3)
148         hasTripleBond = true;
149     }
150     OB_ASSERT(hasTripleBond);
151   }
152 }
153 
154 // A segfault was occuring when a Universal SMILES was output after an InChIfied SMILES.
155 // This was due to short-circuit caching of InChIs on reading. The fix was to limit
156 // the situations when the cached value was used, but also to delete the cached value
157 // in this particular instance.
test_Issue135_UniversalSmiles()158 void test_Issue135_UniversalSmiles()
159 {
160   // Test writing U smiles after I smiles
161   OBConversion conv;
162   conv.SetInFormat("smi");
163   OBMol mol;
164   conv.ReadString(&mol, "C(=O)([O-])C(=O)O");
165   conv.SetOutFormat("smi");
166   conv.SetOptions("I", OBConversion::OUTOPTIONS);
167   std::string res = conv.WriteString(&mol, true);
168   OB_COMPARE(res, "C(=O)(C(=O)O)[O-]");
169   conv.SetOptions("U", OBConversion::OUTOPTIONS);
170   res = conv.WriteString(&mol, true);
171   OB_COMPARE(res, "C(=O)(C(=O)[O-])O");
172 }
173 
174 // Reading an InChI and then adding hydrogens messed up the structure
test_Issue134_InChI_addH()175 void test_Issue134_InChI_addH()
176 {
177   OBConversion conv;
178   conv.SetInFormat("inchi");
179   OBMol mol;
180   conv.ReadString(&mol, "InChI=1S/C2H7NO/c1-2(3)4/h2,4H,3H2,1H3/t2-/m0/s1");
181   OB_ASSERT(!mol.HasData(OBGenericDataType::VirtualBondData));
182   mol.AddHydrogens();
183   conv.SetOutFormat("smi");
184   std::string res = conv.WriteString(&mol, true);
185   OB_COMPARE(res, "C[C@@H](N)O");
186 }
187 
188 // Delete hydrogens should not remove charged or isotopic hydrogens or [H][H] or [Cu][H][Cu]
189 // or hydrogens with assigned atom classes
test_Issue178_DeleteHydrogens()190 void test_Issue178_DeleteHydrogens()
191 {
192   OBConversion conv;
193   conv.SetInFormat("smi");
194   OBMol mol;
195   // Test DeleteHydrogens() and DeleteNonPolarHydrogens()
196   static const char *smi[] = { "C[H]", "[H][H]", "C[1H]", "C[H]C", "C[H+]" };
197   int numHs[] = { 0, 2, 1, 1, 1 };
198   for (int i = 0; i < 5; ++i) {
199     for (int j = 0; j < 2; ++j) {
200       conv.ReadString(&mol, smi[i]);
201       if (j == 0)
202         mol.DeleteHydrogens();
203       else
204         mol.DeleteNonPolarHydrogens();
205       int myNumHs = 0;
206       FOR_ATOMS_OF_MOL(atom, mol)
207         if (atom->GetAtomicNum() == OBElements::Hydrogen)
208           myNumHs++;
209       OB_COMPARE(myNumHs, numHs[i]);
210     }
211   }
212   // Test DeletePolarHydrogens()
213   static const char *smiB[] = { "N[H]", "[H][H]", "N[1H]", "N[H]C", "N[H+]" };
214   int numHsB[] = { 0, 2, 1, 1, 1 };
215   for (int i = 0; i < 5; ++i) {
216     conv.ReadString(&mol, smiB[i]);
217     mol.DeletePolarHydrogens();
218     int myNumHs = 0;
219     FOR_ATOMS_OF_MOL(atom, mol)
220       if (atom->GetAtomicNum() == OBElements::Hydrogen)
221         myNumHs++;
222     OB_COMPARE(myNumHs, numHsB[i]);
223   }
224   // Test atom class
225   // Currently, the SMILES parser does not retain atom classes for hydrogens on reading so...
226   conv.ReadString(&mol, "C[H]");
227   OBPairInteger *ac = new OBPairInteger();
228   ac->SetAttribute("Atom Class");
229   ac->SetValue(99);
230   mol.GetAtom(2)->SetData(ac); // Assign the hydrogen (atom 2) a class of 99
231   mol.DeleteHydrogens();
232   int myNumHs = 0;
233   FOR_ATOMS_OF_MOL(atom, mol)
234     if (atom->GetAtomicNum() == OBElements::Hydrogen)
235       myNumHs++;
236   OB_COMPARE(myNumHs, 1);
237 }
238 
test_Issue305_NumRotors()239 void test_Issue305_NumRotors()
240 {
241   OBMolPtr mol = OBTestUtil::ReadFile("regressiontest_numrotors.mol");
242   OB_COMPARE(mol->NumRotors(), 9); // was returning 4
243 }
244 
test_PR329_Molfile_RGroups()245 void test_PR329_Molfile_RGroups()
246 {
247   // There are several way to get an R Group into a mol file
248   // 1. The existing use of atom maps on dummy atoms in SMILES
249   OBConversion conv;
250   OBMol mol;
251   conv.SetInAndOutFormats("smi", "mol");
252   conv.ReadString(&mol, "C([*:1]CO[*:2]");
253   obErrorLog.SetOutputLevel(obError); // avoid warning about no 2D or 3D coords
254   std::string molfileWithRGP = conv.WriteString(&mol);
255   obErrorLog.SetOutputLevel(obWarning);
256   OB_ASSERT( molfileWithRGP.find("R#") != std::string::npos );
257   OB_ASSERT( molfileWithRGP.find("M  RGP  2   2   1   5   2") != std::string::npos); // i.e. atom 2 is labelled R1, atom 5 is labelled R2
258   // Check negative case
259   conv.ReadString(&mol, "C([*]CO[*]");
260   std::string molfileb = conv.WriteString(&mol);
261   OB_ASSERT( molfileb.find("R#") == std::string::npos );
262   OB_ASSERT( molfileb.find("M  RGP") == std::string::npos);
263 
264   // 2. By reading a molfile that use the R#, RGP notation
265   conv.SetInAndOutFormats("mol", "mol");
266   conv.ReadString(&mol, molfileWithRGP);
267   molfileb = conv.WriteString(&mol);
268   OB_ASSERT( molfileb.find("R#") != std::string::npos );
269   OB_ASSERT( molfileb.find("M  RGP  2   2   1   5   2") != std::string::npos); // i.e. atom 2 is labelled R1, atom 5 is labelled R2
270 
271   // 3. By reading a molfile that specifies the atom alias as Rn, where n is an integer
272   std::string molfileWithAlias = "\n"
273 " OpenBabel07211621152D\n"
274 "\n"
275 "  2  1  0  0  0  0  0  0  0  0999 V2000\n"
276 "    1.0000    0.0000    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0\n"
277 "    0.0000    1.0000    0.0000 *   0  0  0  0  0  0  0  0  0  0  0  0\n"
278 "  1  2  1  0  0  0  0\n"
279 "A    2\n"
280 "R1\n"
281 "M  END";
282   conv.SetInAndOutFormats("mol", "mol");
283   conv.ReadString(&mol, molfileWithAlias);
284   std::string molfile = conv.WriteString(&mol);
285   OB_ASSERT( molfile.find("R#") != std::string::npos );
286   OB_ASSERT( molfile.find("M  RGP  1   2   1") != std::string::npos); // i.e. atom 2 is labelled R1
287   // Check negative case
288   molfileWithAlias = "\n"
289 " OpenBabel07211621152D\n"
290 "\n"
291 "  2  1  0  0  0  0  0  0  0  0999 V2000\n"
292 "    1.0000    0.0000    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0\n"
293 "    0.0000    1.0000    0.0000 *   0  0  0  0  0  0  0  0  0  0  0  0\n"
294 "  1  2  1  0  0  0  0\n"
295 "A    2\n"
296 "R\n"
297 "M  END";
298   conv.SetInAndOutFormats("mol", "mol");
299   obErrorLog.SetOutputLevel(obError); // avoid warning "Alias R was not chemically interpreted"
300   conv.ReadString(&mol, molfileWithAlias);
301   obErrorLog.SetOutputLevel(obWarning);
302   molfile = conv.WriteString(&mol);
303   OB_ASSERT( molfile.find("R#") == std::string::npos );
304   OB_ASSERT( molfile.find("M  RGP") == std::string::npos);
305 
306   // 4. By reading a molfile that specifies the element name as R1, etc.
307   std::string molfileWithRGroupElementName = "\n"
308 " OpenBabel07211621152D\n"
309 "\n"
310 "  2  1  0  0  0  0  0  0  0  0999 V2000\n"
311 "    1.0000    0.0000    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0\n"
312 "    0.0000    1.0000    0.0000 R1  0  0  0  0  0  0  0  0  0  0  0  0\n"
313 "  1  2  1  0  0  0  0\n"
314 "M  END";
315   conv.SetInAndOutFormats("mol", "mol");
316   conv.ReadString(&mol, molfileWithRGroupElementName);
317   molfile = conv.WriteString(&mol);
318   OB_ASSERT( molfile.find("R#") != std::string::npos );
319   OB_ASSERT( molfile.find("M  RGP  1   2   1") != std::string::npos); // i.e. atom 2 is labelled R1
320 }
321 
322 struct SmilesData {
323   const char* inp;
324   const char* out;
325   const char* out_hoption; // if you specify -xh, i.e. "output explicit Hydrogens as such"
326   const char* out_addh_hoption; // if you add hydrogens and then specify "-xh"
327   const char* out_soption; // if you specify -xs, create SMARTS for substructure searching
328 };
329 
test_SMILES_Valence()330 void test_SMILES_Valence()
331 {
332   static const SmilesData smilesData[] = {
333     { "[H]", "", "", "", "[#1]" }, // SMARTS perhaps should be [*H], but not implemented at the moment
334     { "[H][H]", "", "", "","[#1][#1]" },
335     { "[HH]", "", "", "[H][H]", "[#1]" },
336     { "C", "", "", "C([H])([H])([H])[H]", "" },
337     { "[C]", "", "", "", "C" },
338     { "[CH]", "", "", "[C][H]", "C" },
339     { "[CH3]", "", "", "[C]([H])([H])[H]", "C" },
340     { "[CH4]", "C", "C", "C([H])([H])([H])[H]", "C" },
341     { "[CH5]", "", "", "[C]([H])([H])([H])([H])[H]", "C" },
342     { "C[H]", "C", "", "C([H])([H])([H])[H]", "[C!H0]" },
343     { "[C][H]", "[CH]", "", "", "[C!H0]" },
344     { "[CH3][H]", "C", "C[H]", "C([H])([H])([H])[H]", "[C!H0]" },
345     { "[CH2]([H])[H]", "C", "C([H])[H]", "C([H])([H])([H])[H]", "[C!H0!H1]" },
346     { "[U][H]", "[UH]", "", "", "[U!H0]" },
347     { "[UH2]", "", "", "[U]([H])[H]", "[U]" },
348     { "[C@@H](Br)(Cl)I", "", "", "[C@](Br)(Cl)(I)[H]", "[C](Br)(Cl)I" }, // Note: if OB supported it, SMARTS should be [C@@?](Br)(Cl)I
349     { "Br[C@@H](Cl)I", "", "", "Br[C@@](Cl)(I)[H]", "Br[C](Cl)I" }, // Note: if OB supported it, SMARTS should be Br[C@@?](Cl)I
350     { "[C@@](F)(Br)(Cl)I", "", "", "", "" },
351     { "F[C@@](Br)(Cl)I", "", "", "", "" },
352     { "[H][C@@](Br)(Cl)I", "[C@@H](Br)(Cl)I", "", "", "[C@@H](Br)(Cl)I" },
353     { "C[H:1]", "C", "C[H]", "C([H])([H])([H])[H]", "[C!H0]" }, // atom class only shown with -xa
354     { "C[2H]", "", "", "C([2H])([H])([H])[H]", "C[2#1]" },
355     { "c1ccccc1", "", "", "c1(c(c(c(c(c1[H])[H])[H])[H])[H])[H]", "" },
356     { "c1cnccc1", "", "", "c1(c(nc(c(c1[H])[H])[H])[H])[H]", "" },
357     { "c1c[nH]cc1", "", "", "c1(c(n(c(c1[H])[H])[H])[H])[H]", "c1cncc1" },
358     { "F[I]F", "", "", "", "FIF" },
359   };
360   unsigned int size = (unsigned int)(sizeof(smilesData) / sizeof(smilesData[0]));
361   for (unsigned int rep = 0; rep < 4; ++rep) {
362     printf("Rep: %d\n", rep);
363     OBConversion conv;
364     OB_ASSERT(conv.SetInAndOutFormats("smi", "smi"));
365     switch (rep) {
366     case 1: case 2: conv.SetOptions("h", conv.OUTOPTIONS); break;
367     case 3: conv.SetOptions("s", conv.OUTOPTIONS); break;
368     }
369     for (unsigned int i = 0; i < size; ++i) {
370       OBMol mol;
371       OB_ASSERT(conv.ReadString(&mol, smilesData[i].inp));
372       if (rep == 2)
373         mol.AddHydrogens();
374       std::string out = conv.WriteString(&mol, true);
375       const char* mout;
376       switch (rep) {
377       case 0: mout = smilesData[i].out; break;
378       case 1: mout = smilesData[i].out_hoption; break;
379       case 2: mout = smilesData[i].out_addh_hoption; break;
380       case 3: mout = smilesData[i].out_soption; break;
381       }
382       std::string ans = mout[0] ? mout : smilesData[i].inp;
383       printf("  %d %s --> %s (%s)\n", i, smilesData[i].inp, ans.c_str(), out.c_str());
384       OB_COMPARE(out, ans);
385     }
386   }
387 
388   OBConversion conv;
389   OB_ASSERT(conv.SetInAndOutFormats("smi", "smi"));
390   conv.SetOptions("ah", conv.OUTOPTIONS); // write out alias explicitly
391   OBMol mol;
392   conv.ReadString(&mol, "C[H:1]");
393   OB_COMPARE(conv.WriteString(&mol, true), "C[H:1]");
394 }
395 
396 //make sure insertion code gets copied (it wasn't)
test_insertioncode()397 void test_insertioncode()
398 {
399   const char* pdb = "ATOM    266  HB2 ASP L  14      -2.604   8.021  19.867  1.00  0.00           H\n\
400 ATOM    267  CG  ASP L  14      -2.280   6.992  21.697  1.00 18.10           C\n\
401 ATOM    268  OD1 ASP L  14      -1.109   7.431  21.698  1.00 18.97           O\n\
402 ATOM    269  OD2 ASP L  14      -2.735   6.263  22.603  1.00 19.18           O\n\
403 ATOM    270  N   LYS L  14A     -5.804   6.060  21.469  1.00 20.85           N\n\
404 ATOM    271  H   LYS L  14A     -5.589   5.759  20.497  1.00  0.00           H\n\
405 ATOM    272  CA  LYS L  14A     -6.654   5.209  22.296  1.00 22.86           C\n\
406 ATOM    273  HA  LYS L  14A     -7.392   5.923  22.662  1.00  0.00           H\n\
407 ATOM    274  C   LYS L  14A     -6.108   4.607  23.591  1.00 21.70           C\n\
408 ATOM    275  O   LYS L  14A     -6.892   4.228  24.455  1.00 21.72           O\n";
409 
410     OBConversion conv;
411     OB_ASSERT(conv.SetInAndOutFormats("pdb", "pdb"));
412     OBMol mol;
413     conv.ReadString(&mol, pdb);
414     OBMol mol2;
415     mol2 = mol;
416     char i = mol2.GetResidue(1)->GetInsertionCode();
417     OB_COMPARE(i, 'A');
418 }
419 
420 //make sure icode is read by pdbqt
test_insertioncode_pdbqt()421 void test_insertioncode_pdbqt()
422 {
423   const char* pdb = "ATOM    266  HB2 ASP L  14      -2.604   8.021  19.867  1.00  0.00           H\n\
424 ATOM    267  CG  ASP L  14      -2.280   6.992  21.697  1.00 18.10           C\n\
425 ATOM    268  OD1 ASP L  14      -1.109   7.431  21.698  1.00 18.97           O\n\
426 ATOM    269  OD2 ASP L  14      -2.735   6.263  22.603  1.00 19.18           O\n\
427 ATOM    270  N   LYS L  14A     -5.804   6.060  21.469  1.00 20.85           N\n\
428 ATOM    271  H   LYS L  14A     -5.589   5.759  20.497  1.00  0.00           H\n\
429 ATOM    272  CA  LYS L  14A     -6.654   5.209  22.296  1.00 22.86           C\n\
430 ATOM    273  HA  LYS L  14A     -7.392   5.923  22.662  1.00  0.00           H\n\
431 ATOM    274  C   LYS L  14A     -6.108   4.607  23.591  1.00 21.70           C\n\
432 ATOM    275  O   LYS L  14A     -6.892   4.228  24.455  1.00 21.72           O\n";
433 
434     OBConversion conv;
435     OB_ASSERT(conv.SetInAndOutFormats("pdbqt", "pdbqt"));
436     OBMol mol;
437     conv.ReadString(&mol, pdb);
438     OBMol mol2;
439     mol2 = mol;
440     char i = mol2.GetResidue(1)->GetInsertionCode();
441     OB_COMPARE(i, 'A');
442 }
443 
444 // https://github.com/openbabel/openbabel/issues/1794
test_github_issue_1794()445 void test_github_issue_1794()
446 {
447   OBMol mol;
448   OBConversion conv;
449   conv.SetInFormat("smi");
450   conv.ReadString(&mol, "CC[2H]");
451 
452   OBForceField* pFF = OBForceField::FindForceField("UFF");
453   OB_REQUIRE(pFF);
454 
455   OB_ASSERT(pFF->Setup(mol));
456 }
457 
test_github_issue_2111_impl(const std::string & smiles)458 void test_github_issue_2111_impl(const std::string &smiles)
459 {
460   OBMol mol;
461   OBConversion conv;
462   conv.SetInAndOutFormats("smi", "inchi");
463 
464   conv.ReadString(&mol, smiles);
465   mol.DeleteHydrogens();
466   conv.WriteString(&mol);
467 }
468 
469 // https://github.com/openbabel/openbabel/issues/2111
test_github_issue_2111()470 void test_github_issue_2111()
471 {
472   test_github_issue_2111_impl("[H][C@@H](I)F"); // tetrahedral with 2 implicit refs
473   test_github_issue_2111_impl("[H]/N=C/F"); // cis/trans with 2 implicit refs on the left
474   test_github_issue_2111_impl("F/N=C/[H]"); // cis/trans with 2 implicit refs on the right
475 
476   //
477   // Tetrahedral
478   //
479 
480   // example from bug report
481   test_github_issue_2111_impl("[C@@H]12C[C@H](OCC3=CC=CC=C3)[C@@H](COCC3=CC=CC=C3)[C@]1([H])O2");
482 
483   // implicit ref in all positions
484   test_github_issue_2111_impl("[H][C@](C)(F)I");
485   test_github_issue_2111_impl("C[C@]([H])(F)I");
486   test_github_issue_2111_impl("C[C@](F)([H])I");
487   test_github_issue_2111_impl("C[C@](F)(I)[H]");
488 
489   //
490   // Cis/Trans
491   //
492 
493   // implicit ref in all positions
494   test_github_issue_2111_impl("[H]/N(C)=C/F");
495   test_github_issue_2111_impl("C/N([H])=C/F");
496   test_github_issue_2111_impl("F/N=C(/[H])C");
497   test_github_issue_2111_impl("F/N=C(/C)[H]");
498 }
499 
regressionstest(int argc,char * argv[])500 int regressionstest(int argc, char* argv[])
501 {
502   int defaultchoice = 1;
503 
504   int choice = defaultchoice;
505 
506   if (argc > 1) {
507     if(sscanf(argv[1], "%d", &choice) != 1) {
508       printf("Couldn't parse that input as a number\n");
509       return -1;
510     }
511   }
512   // Define location of file formats for testing
513   #ifdef FORMATDIR
514     char env[BUFF_SIZE];
515     snprintf(env, BUFF_SIZE, "BABEL_LIBDIR=%s", FORMATDIR);
516     putenv(env);
517   #endif
518 
519   switch(choice) {
520   case 1:
521     test_Issue135_UniversalSmiles();
522     break;
523   case 221:
524     test_Issue134_InChI_addH();
525     break;
526   case 222:
527     test_Issue178_DeleteHydrogens();
528     break;
529   case 223:
530     test_Issue305_NumRotors();
531     break;
532   case 224:
533     test_PR329_Molfile_RGroups();
534     break;
535   case 225:
536     test_AromaticTripleBond();
537     break;
538   case 226:
539     test_SMILES_Valence();
540     break;
541   case 227:
542     test_OBChemTsfm();
543     break;
544   case 228:
545     test_ChemDraw_Basic();
546     break;
547   case 240:
548     test_Fix1912_PDBReading();
549     break;
550   case 241:
551     test_insertioncode();
552     break;
553   case 242:
554     test_insertioncode_pdbqt();
555     break;
556   case 1794:
557     test_github_issue_1794();
558     break;
559   case 2111:
560     test_github_issue_2111();
561     break;
562   //case N:
563   //  YOUR_TEST_HERE();
564   //  Remember to update CMakeLists.txt with the number of your test
565   //  break;
566   default:
567     cout << "Test number " << choice << " does not exist!\n";
568     return -1;
569   }
570 
571   return 0;
572 }
573 
574