Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rudimentary webnwb testing #42

Open
yarikoptic opened this issue Feb 22, 2023 · 5 comments
Open

Add rudimentary webnwb testing #42

yarikoptic opened this issue Feb 22, 2023 · 5 comments

Comments

@yarikoptic
Copy link
Member

https://github.com/brainsatplay/webnwb#loading-a-local-file gives an example

const file = io.load(filename)

Since I know no JS, @garrettmflynn, would you be so kind to guide me and @jwodder on

  • the preferable way to execute such JS from console (IIRC I used node in some other project for that)
  • if that load is lazy and does not really read much of metadata or data until asked, is there any other "generic" (applicable to any .nwb file) action to perform to quickly check that webnwb is capable of reading the .nwb at hand.
@garrettmflynn
Copy link

garrettmflynn commented Feb 23, 2023

Just out of curiosity, are y'all intending to use webnwb in Node.js in the long run? Generally, I've created the library for the browser—though it should be possible to fully support node with a bit of finessing.

1. Execute WebNWB

Generally, if you'd like to use webnwb using Node.js, you'd install the library using Node Package Manager (NPM), import that in a .js file (e.g. a file called main.js with const nwb = require('webnwb') or import nwb from 'webnwb') and insert the code you'd like to execute, then run node main.js in the console.

Internally in the webnwb and hdf5-io repos, I'm using Jest for testing. For this, I'm writing a test file and running npm run test, which executes the Jest CLI based on the script property of the package.json file.

To help you both get started, I created a simple test repo using the browser and Node.js. It seems like there are some leftover bugs for streaming NWB files in Node.js and loading local files. The installation and execution steps are contained in that repo's README.md file. Generally, just run npm i then npm start for Node.js, and use Visual Studio Code's Live Server extension for the HTML file.

Check WebNWB Will Work

You should be able to check that required properties such as identifier or nwb_version are present.

Are you expecting to load NWB files that aren't able to be read by webnwb?

@yarikoptic
Copy link
Member Author

Just out of curiosity, are y'all intending to use webnwb in Node.js in the long run?

ATM I have no immediate "use" cases for webnwb so the answer would be No I guess ;) in the effort of this particular (dandisets-healthstatus) effort we are trying to make sure that all .nwb files uploaded to the archive could be at least opened with most common libraries for .nwb (pynwb, matnwb, and now webnwb ;) ). Depending on how people would like to use - in browsers pure JS or via node, we might want to adjust/extend our tests battery.

To help you both get started, I created a simple test repo using the browser and Node.js. It seems like there are some leftover bugs for streaming NWB files in Node.js and loading local files.

THANK YOU!

Indeed the script errors out for me
❯ git clone https://github.com/garrettmflynn/webnwb-test
Cloning into 'webnwb-test'...
remote: Enumerating objects: 14, done.
remote: Counting objects: 100% (14/14), done.
remote: Compressing objects: 100% (11/11), done.
remote: Total 14 (delta 0), reused 14 (delta 0), pack-reused 0
Receiving objects: 100% (14/14), 1.51 MiB | 1.38 MiB/s, done.
❯ cd webnwb-test
README.md  dir/  index.html  index.js  localFerguson.nwb  package-lock.json  package.json  polyfill.js
❯ npm install

added 10 packages, and audited 11 packages in 28s

3 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
npm install  8.35s user 1.71s system 35% cpu 28.376 total
changes on filesystem:                                                                                                           
 package-lock.json | 3 +++
❯ node index.js
[webnwb]: Generated core in 4.661597013473511 ms
[webnwb]: Loading hdmf-common extension.
[webnwb]: Generated hdmf-common in 0.628790020942688 ms
[webnwb]: Loading hdmf-experimental extension.
[webnwb]: Generated hdmf-experimental in 0.2535330057144165 ms
file [String: '2.4.0'] [String: 'WebNWB_Test_a3x1l'] [String: 'Just a test.'] 


[webnwb]: Generated core in 8.464712023735046 ms
[webnwb]: Loading hdmf-common extension.
[webnwb]: Generated hdmf-common in 0.5233869552612305 ms
remote [String: '2.2.5'] [String: 'b1c5c436-b615-459b-bf54-0ef91d908139'] [String: 'Ferguson et al. PYR1.abf [Pyramidal cell 1, strongly adapting]'] 


HDF5-DIAG: Error detected in HDF5 (1.12.1) thread 0:
  #000: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5F.c line 620 in H5Fopen(): unable to open file
    major: File accessibility
    minor: Unable to open file
  #001: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5VLcallback.c line 3502 in H5VL_file_open(): failed to iterate over available VOL connector plugins
    major: Virtual Object Layer
    minor: Iteration failed
  #002: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5PLpath.c line 579 in H5PL__path_table_iterate(): can't iterate over plugins in plugin path '(null)'
    major: Plugin for dynamically loaded library
    minor: Iteration failed
  #003: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5PLpath.c line 620 in H5PL__path_table_iterate_process_path(): can't open directory: /usr/local/hdf5/lib/plugin
    major: Plugin for dynamically loaded library
    minor: Can't open directory or file
  #004: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5VLcallback.c line 3351 in H5VL__file_open(): open failed
    major: Virtual Object Layer
    minor: Can't open object
  #005: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5VLnative_file.c line 97 in H5VL__native_file_open(): unable to open file
    major: File accessibility
    minor: Unable to open file
  #006: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5Fint.c line 1835 in H5F_open(): unable to open file: name = 'localFerguson.nwb', tent_flags = 0
    major: File accessibility
    minor: Unable to open file
  #007: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5FD.c line 723 in H5FD_open(): open failed
    major: Virtual File Layer
    minor: Unable to initialize object
  #008: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5FDsec2.c line 355 in H5FD__sec2_open(): unable to open file: name = 'localFerguson.nwb', errno = 44, error message = 'No such file or directory', flags = 0, o_flags = 0
    major: File accessibility
    minor: Unable to open file
[hdf5-io]: Could not open file localFerguson.nwb
[webnwb]: Generated core in 7.830779016017914 ms
[webnwb]: Loading hdmf-common extension.
[webnwb]: Generated hdmf-common in 0.44422799348831177 ms
[webnwb]: Loading hdmf-experimental extension.
[webnwb]: Generated hdmf-experimental in 0.2418140172958374 ms

WEBNWB ERROR: Failed to load local file properly... undefined

as I care only about opening/loading ATM, I took only opening part and with help of chatgpt I added loading filename from cmdline

to get this script
import './polyfill.js' // This is required since the bundle assumes there is a global Blob object
import nwb from 'webnwb'
import process from 'process'

const args = process.argv.slice(2)
const filename = args[0]

const io = new nwb.NWBHDF5IO()
const file = await io.load(filename)

console.log('file', file.nwb_version, file.identifier,file.session_description, '\n\n')
which failed similarly on a sample .nwb file (fuse mounted from s3 url locally)
❯ node opennwb.js ../../dandisets-fused/000003/sub-YutaMouse20/sub-YutaMouse20_ses-YutaMouse20-140321_behavior+ecephys.nwb
HDF5-DIAG: Error detected in HDF5 (1.12.1) thread 0:
  #000: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5F.c line 620 in H5Fopen(): unable to open file
    major: File accessibility
    minor: Unable to open file
  #001: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5VLcallback.c line 3502 in H5VL_file_open(): failed to iterate over available VOL connector plugins
    major: Virtual Object Layer
    minor: Iteration failed
  #002: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5PLpath.c line 579 in H5PL__path_table_iterate(): can't iterate over plugins in plugin path '(null)'
    major: Plugin for dynamically loaded library
    minor: Iteration failed
  #003: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5PLpath.c line 620 in H5PL__path_table_iterate_process_path(): can't open directory: /usr/local/hdf5/lib/plugin
    major: Plugin for dynamically loaded library
    minor: Can't open directory or file
  #004: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5VLcallback.c line 3351 in H5VL__file_open(): open failed
    major: Virtual Object Layer
    minor: Can't open object
  #005: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5VLnative_file.c line 97 in H5VL__native_file_open(): unable to open file
    major: File accessibility
    minor: Unable to open file
  #006: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5Fint.c line 1835 in H5F_open(): unable to open file: name = '../../dandisets-fused/000003/sub-YutaMouse20/sub-YutaMouse20_ses-YutaMouse20-140321_behavior+ecephys.nwb', tent_flags = 0
    major: File accessibility
    minor: Unable to open file
  #007: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5FD.c line 723 in H5FD_open(): open failed
    major: Virtual File Layer
    minor: Unable to initialize object
  #008: /home/brian/dev/libhdf5-wasm/wasm_build/1_12_1/_deps/hdf5-src/src/H5FDsec2.c line 355 in H5FD__sec2_open(): unable to open file: name = '../../dandisets-fused/000003/sub-YutaMouse20/sub-YutaMouse20_ses-YutaMouse20-140321_behavior+ecephys.nwb', errno = 44, error message = 'No such file or directory', flags = 0, o_flags = 0
    major: File accessibility
    minor: Unable to open file
[hdf5-io]: Could not open file ../../dandisets-fused/000003/sub-YutaMouse20/sub-YutaMouse20_ses-YutaMouse20-140321_behavior+ecephys.nwb
[webnwb]: Generated core in 12.945061028003693 ms
[webnwb]: Loading hdmf-common extension.
[webnwb]: Generated hdmf-common in 0.8175250291824341 ms
[webnwb]: Loading hdmf-experimental extension.
[webnwb]: Generated hdmf-experimental in 0.37734299898147583 ms
file [String: '2.4.0'] undefined undefined 

Are you expecting to load NWB files that aren't able to be read by webnwb?

sorry , I am not following... FWIW: I am expecting webnwb to be able to load and read any legit .nwb file. I expect that only legit .nwb files would be uploaded to dandiarchive.

@garrettmflynn
Copy link

Thank you for explaining the goals of the repo. Now your questions make much more sense.

Can you explain to me how MatNWB and PyNWB are currently being checked? That way, I can propose an analogous solution to what you already have for those.

There will be very few inconsistencies between the browser and Node—aside from the latter being more difficult to set up read/stream and write operations, while also being better to access local files without user input. As such, I'd expect we could get by with finalized tests for Node.

@yarikoptic
Copy link
Member Author

Can you explain to me how MatNWB and PyNWB are currently being checked? That way, I can propose an analogous solution to what you already have for those.

ATM we are trying to stay as basic (as minimal/simplest invocation) as possible to test the most basic functionality - being able to open an .nwb file without blowing up, but even that still shows to be challenging ;-)

with pynwb.NWBHDF5IO(sys.argv[1], "r", load_namespaces=True) as reader:
    nwbfile = reader.read()
assert repr(nwbfile)
assert str(nwbfile)
generateCore()
nwb = nwbRead(f, 'savedir', '../out')

but we might need to change, following the discussion in NeurodataWithoutBorders/matnwb#493 and NeurodataWithoutBorders/matnwb#491 .

Unfortunately our full sweep process is still too slow due to need to be at large serial so we can't quickly change, but soon will do after current sweep through all dandisets is done and we get our https://github.com/dandi/dandisets-healthstatus#readme updated.

@garrettmflynn
Copy link

garrettmflynn commented Feb 24, 2023

Got it. The conversion of the PyNWB test for WebNWB would look like the following:

import './polyfill.js' // This is required since the bundle assumes there is a global Blob object
import nwb from 'webnwb'
import process from 'process'

const args = process.argv.slice(2)
const filename = args[0]

const io = new nwb.NWBHDF5IO()
const file = await io.load(filename)

if (!file) throw new Error(`File ${filename} could not be loaded`)

// // Works in current version (see note in text below)
// const identifier = async file.identifier
// if (!identifier) throw new Error(`File ${filename} could not be loaded`)

However, I've noticed that this currently would not be thrown for files that currently error, as they'll return an empty NWBFile object (which passes this check), which is why I suggested checking properties like identifier that are required but not set by the API if missing.

I will fix this in the next release of WebNWB so that files that cannot be loaded / streamed return an undefined value and, therefore, trigger the above check.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants