From 0f9af871eada553d286d143607441cdb9e1ce467 Mon Sep 17 00:00:00 2001 From: Gaurav Vaidya Date: Wed, 24 Apr 2024 04:00:02 -0400 Subject: [PATCH] Added test for normalization. --- .../brochu_2003_normalization.json | 221 ++++++++++++++++++ test/normalization.js | 124 ++++++++++ 2 files changed, 345 insertions(+) create mode 100644 test/examples/correct/normalization/brochu_2003_normalization.json create mode 100644 test/normalization.js diff --git a/test/examples/correct/normalization/brochu_2003_normalization.json b/test/examples/correct/normalization/brochu_2003_normalization.json new file mode 100644 index 00000000..8d64abe9 --- /dev/null +++ b/test/examples/correct/normalization/brochu_2003_normalization.json @@ -0,0 +1,221 @@ +{ + "@context": "https://www.phyloref.org/phyx.js/context/v1.0.0/phyx.json", + "doi": "10.5281/zenodo.4562685", + "source": { + "authors": [ + { + "firstname": "Gaurav", + "lastname": "Vaidya" + } + ], + "year": 2021, + "title": "Digital representation of some of the clade definitions in Brochu 2003 in the Phyloreference Exchange (Phyx) format", + "journal": { + "name": "Zenodo" + }, + "identifier": [ + { + "type": "doi", + "id": "10.5281/zenodo.4562685" + } + ] + }, + "phylogenies": [ + { + "newick": "(Parasuchia,(rauisuchians,Aetosauria,(sphenosuchians,(protosuchians,(mesosuchians,(Hylaeochampsa,Aegyptosuchus,Stomatosuchus,(Allodaposuchus,('Gavialis gangeticus',(('Diplocynodon ratelii',('Alligator mississippiensis','Caiman crocodilus')Alligatoridae)Alligatoroidea,('Tomistoma schlegelii',('Osteolaemus tetraspis','Crocodylus niloticus')Crocodylinae)Crocodylidae)Brevirostres)Crocodylia))Eusuchia)Mesoeucrocodylia)Crocodyliformes)Crocodylomorpha))root;", + "label": "Fig 1 from Brochu 2003", + "@id": "#phylogeny0", + "source": { + "type": "article", + "title": "Phylogenetic approaches toward crocodylian history", + "authors": [ + { + "name": "Christopher A. Brochu", + "alternate": [ + "Brochu, Christopher A." + ], + "firstname": "Christopher", + "middlename": "A.", + "lastname": "Brochu" + } + ], + "year": 2003, + "figure": "1", + "identifier": [ + { + "type": "doi", + "id": "10.1146/annurev.earth.31.100901.141308" + } + ], + "link": [ + { + "url": "https://www.annualreviews.org/doi/10.1146/annurev.earth.31.100901.141308" + } + ], + "journal": { + "name": "Annual Review of Earth and Planetary Sciences", + "volume": "31", + "pages": "357--397", + "identifier": [ + { + "type": "eISSN", + "id": "1545-4495" + } + ] + } + } + } + ], + "phylorefs": [ + { + "@id": "#Alligatoridae1_same", + "label": "Alligatoridae", + "scientificNameAuthorship": { + "bibliographicCitation": "(Cuvier 1807)" + }, + "phylorefType": "phyloref:PhyloreferenceUsingMinimumClade", + "definition": "Alligatoridae (Cuvier 1807).\n\nLast common ancestor of Alligator mississippiensis and Caiman crocodilus and all of its descendents.", + "definitionSource": { + "type": "article", + "title": "Phylogenetic approaches toward crocodylian history", + "authors": [ + { + "name": "Christopher A. Brochu", + "alternate": [ + "Brochu, Christopher A." + ], + "firstname": "Christopher", + "middlename": "A.", + "lastname": "Brochu" + } + ], + "year": 2003, + "figure": "1", + "identifier": [ + { + "type": "doi", + "id": "10.1146/annurev.earth.31.100901.141308" + } + ], + "link": [ + { + "url": "https://www.annualreviews.org/doi/10.1146/annurev.earth.31.100901.141308" + } + ], + "journal": { + "name": "Annual Review of Earth and Planetary Sciences", + "volume": "31", + "pages": "357--397", + "identifier": [ + { + "type": "eISSN", + "id": "1545-4495" + } + ] + } + }, + "internalSpecifiers": [ + { + "@type": "http://rs.tdwg.org/ontology/voc/TaxonConcept#TaxonConcept", + "hasName": { + "@type": "http://rs.tdwg.org/ontology/voc/TaxonName#TaxonName", + "nomenclaturalCode": "http://rs.tdwg.org/ontology/voc/TaxonName#ICZN", + "label": "Caiman crocodilus", + "nameComplete": "Caiman crocodilus", + "genusPart": "Caiman", + "specificEpithet": "crocodilus" + } + }, + { + "@type": "http://rs.tdwg.org/ontology/voc/TaxonConcept#TaxonConcept", + "hasName": { + "@type": "http://rs.tdwg.org/ontology/voc/TaxonName#TaxonName", + "nomenclaturalCode": "http://rs.tdwg.org/ontology/voc/TaxonName#ICZN", + "label": "Alligator mississippiensis", + "nameComplete": "Alligator mississippiensis", + "genusPart": "Alligator", + "specificEpithet": "mississippiensis" + }, + "label": "Alligator mississippiensis" + } + ], + "externalSpecifiers": [] + }, + { + "@id": "#Alligatoridae2_same", + "label": "Alligatoridae", + "scientificNameAuthorship": { + "bibliographicCitation": "(Cuvier 1807)" + }, + "phylorefType": "phyloref:PhyloreferenceUsingMinimumClade", + "definition": "Alligatoridae (Cuvier 1807).\n\nLast common ancestor of Alligator mississippiensis and Caiman crocodilus and all of its descendents.", + "definitionSource": { + "type": "article", + "title": "Phylogenetic approaches toward crocodylian history", + "authors": [ + { + "name": "Christopher A. Brochu", + "alternate": [ + "Brochu, Christopher A." + ], + "firstname": "Christopher", + "middlename": "A.", + "lastname": "Brochu" + } + ], + "year": 2003, + "figure": "1", + "identifier": [ + { + "type": "doi", + "id": "10.1146/annurev.earth.31.100901.141308" + } + ], + "link": [ + { + "url": "https://www.annualreviews.org/doi/10.1146/annurev.earth.31.100901.141308" + } + ], + "journal": { + "name": "Annual Review of Earth and Planetary Sciences", + "volume": "31", + "pages": "357--397", + "identifier": [ + { + "type": "eISSN", + "id": "1545-4495" + } + ] + } + }, + "internalSpecifiers": [ + { + "@type": "http://rs.tdwg.org/ontology/voc/TaxonConcept#TaxonConcept", + "hasName": { + "@type": "http://rs.tdwg.org/ontology/voc/TaxonName#TaxonName", + "label": "Caiman crocodilus", + "nameComplete": "Caiman crocodilus", + "genusPart": "Caiman", + "specificEpithet": "crocodilus", + "nomenclaturalCode": "http://rs.tdwg.org/ontology/voc/TaxonName#ICZN" + }, + "label": "Caiman crocodilus" + }, + { + "@type": "http://rs.tdwg.org/ontology/voc/TaxonConcept#TaxonConcept", + "hasName": { + "@type": "http://rs.tdwg.org/ontology/voc/TaxonName#TaxonName", + "nomenclaturalCode": "http://rs.tdwg.org/ontology/voc/TaxonName#ICZN", + "label": "Alligator mississippiensis", + "nameComplete": "Alligator mississippiensis", + "genusPart": "Alligator", + "specificEpithet": "mississippiensis" + }, + "label": "Alligator mississippiensis" + } + ], + "externalSpecifiers": [] + } + ], + "defaultNomenclaturalCodeIRI": "http://rs.tdwg.org/ontology/voc/TaxonName#ICZN" +} diff --git a/test/normalization.js b/test/normalization.js new file mode 100644 index 00000000..b5b8b80b --- /dev/null +++ b/test/normalization.js @@ -0,0 +1,124 @@ +/* + * Test normalization on examples/correct/normalization files. + */ + +const fs = require('fs'); +const path = require('path'); + +const chai = require('chai'); + +const phyx = require('../src'); + +const expect = chai.expect; + +/** + * When making the comparisons, we need to remove the `@id`s which would otherwise be different + * between the phylorefs being compared. + * + * @param phyloref The phyloref whose '@id' needs to be removed. + */ +function removeId(phyloref) { + // Shallow copy the phyloref. + const copiedPhyloref = Object.assign({}, phyloref); + // Delete the '@id'. + delete copiedPhyloref['@id']; + return copiedPhyloref; +} + + +/** + * Test whether normalization of phyloreferences and phylogenies work as expected. + * This test does not cover Phyx normalization. + */ + +describe('Phyloref and phylogeny normalization', function () { + describe('Test all normalization Phyx files', function () { + /* + * Normalization Phyx files consist of a number of phyloreferences and phylogenies. We can + * test them by confirming: + * - every phyloref or phylogeny should be DIFFERENT from every other. + * - every phyloref or phylogeny whose `@id` ends with `_same` should be IDENTICAL to + * every other phyloref or phylogeny whose `@id` ends with `_same` after NORMALIZATION (to + * test non-same phyloref or phylogeny files, they should be placed in different files). + * - every phyloref or phylogeny whose `@id` ends with `_different` should be DIFFERENT to + * every phyloref or phylogeny whose `@id` ends with `_same` even after NORMALIZATION. + */ + const normalizationExamples = fs + .readdirSync(path.resolve(__dirname, './examples/correct/normalization')) + .filter(filename => filename.endsWith('.json')); + + normalizationExamples.forEach((example) => { + const basename = path.resolve(__dirname, './examples/correct/normalization', path.parse(example).name); + const jsonFilename = `${basename}.json`; + + describe(`Normalization test file '${example}'`, function () { + const phyxDoc = JSON.parse(fs.readFileSync(jsonFilename)); + const phylorefs = phyxDoc.phylorefs || []; + const samePhylorefs = phylorefs.filter(p => (p['@id'] || '').endsWith('_same')); + const differentPhylorefs = phylorefs.filter(p => (p['@id'] || '').endsWith('_different')); + + // We don't need phylogeny normalization yet, so there's no point in testing them. + describe('Test phylogenies', function () { + it("These tests have not yet been implemented since we don't have an urgent need for them."); + }); + + // So we only focus on phyloreference normalization. + describe('Test phyloreferences', function () { + it('should have multiple same phyloreferences for testing', function () { + expect(samePhylorefs).to.not.be.empty; + }); + + // No two phyloreferences in a normalization file should be deeply identical to each + // other, otherwise the test will be pointless. + phylorefs.forEach((phyloref1) => { + phylorefs.forEach((phyloref2) => { + if (phyloref1 === phyloref2) return; + expect(removeId(phyloref1)) + .to + .not + .deep + .equal(removeId(phyloref2), + 'No two phyloreferences in a single normalization file should be identical.'); + }); + }); + + // Every pair of `_same` phyloreferences should be different. + samePhylorefs.forEach((phyloref1) => { + samePhylorefs.forEach((phyloref2) => { + if (phyloref1 === phyloref2) return; + expect( + removeId(phyx.PhylorefWrapper.normalize(phyloref1)) + ) + .to + .deep + .equal( + removeId(phyx.PhylorefWrapper.normalize(phyloref2)), + `Expected phyloref ${phyloref1['@id']} to deeply equal ${phyloref2['@id']} ` + + 'after normalization' + ); + }); + }); + + // Every pair of `_different` phyloreferences should be different from every `_same` + // phyloreference, even after normalization. + differentPhylorefs.forEach((phyloref1) => { + samePhylorefs.forEach((phyloref2) => { + if (phyloref1 === phyloref2) return; + expect( + removeId(phyx.PhylorefWrapper.normalize(phyloref1)) + ) + .to + .not + .deep + .equal( + removeId(phyx.PhylorefWrapper.normalize(phyloref2)), + `Expected phyloref ${phyloref1['@id']} to not deeply equal ${phyloref2['@id']} ` + + 'after normalization' + ); + }); + }); + }); + }); + }); + }); +});