summaryrefslogtreecommitdiffstats
path: root/tools/h5diff
diff options
context:
space:
mode:
authorJonathan Kim <jkm@hdfgroup.org>2010-09-17 00:22:29 (GMT)
committerJonathan Kim <jkm@hdfgroup.org>2010-09-17 00:22:29 (GMT)
commit3bfb10b2fc080b138ed8fb934c59b383fc292c98 (patch)
tree50daf2821c1f48a77983fc95a459888f4911ce55 /tools/h5diff
parent696675029e4bd3461bc175fb9d9b3406767acbfd (diff)
downloadhdf5-3bfb10b2fc080b138ed8fb934c59b383fc292c98.zip
hdf5-3bfb10b2fc080b138ed8fb934c59b383fc292c98.tar.gz
hdf5-3bfb10b2fc080b138ed8fb934c59b383fc292c98.tar.bz2
[svn-r19411] Purpose:
Add extra test cases for Bug1975 h5diff - support recursive comparison on group when specified as an object Description: Merged from hdf5 trunk (r19409) Additional tests for combination of group recursive and --follow-symlinks with multi-linked external links with several files (same name/strucure). Tested: jam, amani, heiwa
Diffstat (limited to 'tools/h5diff')
-rw-r--r--tools/h5diff/CMakeLists.txt21
-rw-r--r--tools/h5diff/h5diffgentest.c336
-rw-r--r--tools/h5diff/testfiles/h5diff_515.txt24
-rw-r--r--tools/h5diff/testfiles/h5diff_516.txt32
-rw-r--r--tools/h5diff/testfiles/h5diff_517.txt15
-rw-r--r--tools/h5diff/testfiles/h5diff_518.txt23
-rw-r--r--tools/h5diff/testfiles/h5diff_grp_recurse_ext1.h5bin0 -> 8120 bytes
-rw-r--r--tools/h5diff/testfiles/h5diff_grp_recurse_ext2-1.h5bin0 -> 4296 bytes
-rw-r--r--tools/h5diff/testfiles/h5diff_grp_recurse_ext2-2.h5bin0 -> 5640 bytes
-rw-r--r--tools/h5diff/testfiles/h5diff_grp_recurse_ext2-3.h5bin0 -> 2464 bytes
-rwxr-xr-xtools/h5diff/testh5diff.sh18
11 files changed, 468 insertions, 1 deletions
diff --git a/tools/h5diff/CMakeLists.txt b/tools/h5diff/CMakeLists.txt
index a22e0e3..924c4ec 100644
--- a/tools/h5diff/CMakeLists.txt
+++ b/tools/h5diff/CMakeLists.txt
@@ -194,6 +194,10 @@ IF (BUILD_TESTING)
h5diff_danglelinks2.h5
h5diff_grp_recurse1.h5
h5diff_grp_recurse2.h5
+ h5diff_grp_recurse_ext1.h5
+ h5diff_grp_recurse_ext2-1.h5
+ h5diff_grp_recurse_ext2-2.h5
+ h5diff_grp_recurse_ext2-3.h5
h5diff_exclude1-1.h5
h5diff_exclude1-2.h5
h5diff_exclude2-1.h5
@@ -304,6 +308,11 @@ SET (DANGLE_LINK_FILE1 h5diff_danglelinks1.h5)
SET (DANGLE_LINK_FILE2 h5diff_danglelinks2.h5)
SET (GRP_RECURSE_FILE1 h5diff_grp_recurse1.h5)
SET (GRP_RECURSE_FILE2 h5diff_grp_recurse2.h5)
+# group recursive - same structure via external links through files
+SET (GRP_RECURSE1_EXT h5diff_grp_recurse_ext1.h5)
+SET (GRP_RECURSE2_EXT1 h5diff_grp_recurse_ext2-1.h5)
+SET (GRP_RECURSE2_EXT2 h5diff_grp_recurse_ext2-2.h5)
+SET (GRP_RECURSE2_EXT3 h5diff_grp_recurse_ext2-3.h5)
# same structure, same obj name with different value
SET (EXCLUDE_FILE1_1 h5diff_exclude1-1.h5)
SET (EXCLUDE_FILE1_2 h5diff_exclude1-2.h5)
@@ -732,6 +741,18 @@ ADD_H5_TEST (h5diff_512 -v --follow-symlinks ${GRP_RECURSE_FILE1} ${GRP_RECURSE_
ADD_H5_TEST (h5diff_513 -v ${GRP_RECURSE_FILE1} ${GRP_RECURSE_FILE2} /slink_grp10 /slink_grp11)
ADD_H5_TEST (h5diff_514 -v --follow-symlinks ${GRP_RECURSE_FILE1} ${GRP_RECURSE_FILE2} /slink_grp10 /slink_grp11)
+###############################################################################
+# Test for group recursive diff via multi-linked external links
+# With follow-symlinks, file $GRP_RECURSE1_EXT and $GRP_RECURSE2_EXT1 should
+# be same with the external links.
+###############################################################################
+# file vs file
+ADD_H5_TEST (h5diff_515 -v ${GRP_RECURSE1_EXT} ${GRP_RECURSE2_EXT1})
+ADD_H5_TEST (h5diff_516 -v --follow-symlinks ${GRP_RECURSE1_EXT} ${GRP_RECURSE2_EXT1})
+# group vs group
+ADD_H5_TEST (h5diff_517 -v ${GRP_RECURSE1_EXT} ${GRP_RECURSE2_EXT1} /g1)
+ADD_H5_TEST (h5diff_518 -v --follow-symlinks ${GRP_RECURSE1_EXT} ${GRP_RECURSE2_EXT1} /g1)
+
# ##############################################################################
# # Exclude path (--exclude-path)
diff --git a/tools/h5diff/h5diffgentest.c b/tools/h5diff/h5diffgentest.c
index 29823e4..a3e1ef2 100644
--- a/tools/h5diff/h5diffgentest.c
+++ b/tools/h5diff/h5diffgentest.c
@@ -57,6 +57,11 @@
#define DANGLE_LINK_FILE2 "h5diff_danglelinks2.h5"
#define GRP_RECURSE_FILE1 "h5diff_grp_recurse1.h5"
#define GRP_RECURSE_FILE2 "h5diff_grp_recurse2.h5"
+/* same structure via external links through files */
+#define GRP_RECURSE1_EXT "h5diff_grp_recurse_ext1.h5"
+#define GRP_RECURSE2_EXT1 "h5diff_grp_recurse_ext2-1.h5"
+#define GRP_RECURSE2_EXT2 "h5diff_grp_recurse_ext2-2.h5"
+#define GRP_RECURSE2_EXT3 "h5diff_grp_recurse_ext2-3.h5"
/* same structure, same obj name with different value */
#define EXCLUDE_FILE1_1 "h5diff_exclude1-1.h5"
#define EXCLUDE_FILE1_2 "h5diff_exclude1-2.h5"
@@ -108,6 +113,7 @@ static int test_external_links(const char *fname1, const char *fname2);
static int test_ext2soft_links(const char *fname1, const char *fname2);
static int test_dangle_links(const char *fname1, const char *fname2);
static int test_group_recurse(const char *fname1, const char *fname2);
+static int test_group_recurse2();
static int test_exclude_obj1(const char *fname1, const char *fname2);
static int test_exclude_obj2(const char *fname1, const char *fname2);
@@ -160,11 +166,11 @@ int main(void)
test_dangle_links(DANGLE_LINK_FILE1, DANGLE_LINK_FILE2);
test_group_recurse(GRP_RECURSE_FILE1, GRP_RECURSE_FILE2);
+ test_group_recurse2();
test_exclude_obj1(EXCLUDE_FILE1_1, EXCLUDE_FILE1_2);
test_exclude_obj2(EXCLUDE_FILE2_1, EXCLUDE_FILE2_2);
-
return 0;
}
@@ -2305,6 +2311,334 @@ out:
/*-------------------------------------------------------------------------
*
+* Purpose:
+* For testing comparing group member objects recursively via multiple
+* linked external links
+*
+* Programmer: Jonathan Kim (Sep 16, 2010)
+*
+*-------------------------------------------------------------------------*/
+#define GRP_R_DSETNAME1 "dset1"
+#define GRP_R_DSETNAME2 "dset2"
+static int test_group_recurse2()
+{
+ hid_t fileid1;
+ hid_t grp1=0, grp2;
+ hid_t grp3=0;
+ hid_t grp4=0;
+ hid_t tid;
+ hid_t dset1, dset2;
+ hid_t datatype, dataspace; /* handles */
+ hid_t fileid2;
+ hid_t fileid3;
+ hid_t fileid4;
+ hid_t fileid4_1;
+ hsize_t dimsf[2]; /* dataset dimensions */
+ herr_t status=0;
+ int data1[4][2] = {{0,0},{1,1},{2,2},{3,3}};
+ int data2[4][2] = {{0,0},{0,1},{0,2},{3,3}};
+ int i, j;
+
+ /*-----------------------------------------------------------------------
+ * FILE 1
+ *------------------------------------------------------------------------*/
+ /*
+ * Create a new file using H5F_ACC_TRUNC access,
+ * default file creation properties, and default file
+ * access properties.
+ */
+ fileid1 = H5Fcreate(GRP_RECURSE1_EXT, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
+
+ /*-----------------------------------------------------------------------
+ * Groups
+ *------------------------------------------------------------------------*/
+ grp1 = H5Gcreate2(fileid1, "/g1", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
+ if (grp1 < 0)
+ {
+ fprintf(stderr, "Error: %s> H5Gcreate2 failed.\n", GRP_RECURSE1_EXT);
+ status = FAIL;
+ goto out;
+ }
+
+ grp2 = H5Gcreate2(grp1, "g2", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
+ if (grp2 < 0)
+ {
+ fprintf(stderr, "Error: %s> H5Gcreate2 failed.\n", GRP_RECURSE1_EXT);
+ status = FAIL;
+ goto out;
+ }
+
+ grp3 = H5Gcreate2(grp2, "g3", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
+ if (grp3 < 0)
+ {
+ fprintf(stderr, "Error: %s> H5Gcreate2 failed.\n", GRP_RECURSE1_EXT);
+ status = FAIL;
+ goto out;
+ }
+
+ grp4 = H5Gcreate2(grp3, "g4", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
+ if (grp4 < 0)
+ {
+ fprintf(stderr, "Error: %s> H5Gcreate2 failed.\n", GRP_RECURSE1_EXT);
+ status = FAIL;
+ goto out;
+ }
+
+
+ /*-----------------------------------------------------------------------
+ * Datasets
+ *------------------------------------------------------------------------*/
+ /*
+ * Describe the size of the array and create the data space for fixed
+ * size dataset.
+ */
+ dimsf[0] = 4;
+ dimsf[1] = 2;
+ dataspace = H5Screate_simple(2, dimsf, NULL);
+
+ /*
+ * Define datatype for the data in the file.
+ * We will store little endian INT numbers.
+ */
+ datatype = H5Tcopy(H5T_NATIVE_INT);
+ status = H5Tset_order(datatype, H5T_ORDER_LE);
+
+ /*---------------
+ * dset1
+ */
+ /*
+ * Create a new dataset within the file using defined dataspace and
+ * datatype and default dataset creation properties.
+ */
+ dset1 = H5Dcreate2(fileid1, GRP_R_DSETNAME1, datatype, dataspace,
+ H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
+
+ /*
+ * Write the data to the dataset using default transfer properties.
+ */
+ status = H5Dwrite(dset1, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, data1);
+ H5Dclose(dset1);
+
+ /*---------------
+ * dset1
+ */
+ /*
+ * Create a new dataset within the file using defined dataspace and
+ * datatype and default dataset creation properties.
+ */
+ dset1 = H5Dcreate2(grp3, GRP_R_DSETNAME1, datatype, dataspace,
+ H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
+
+ /*
+ * Write the data to the dataset using default transfer properties.
+ */
+ status = H5Dwrite(dset1, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, data1);
+
+ /*---------------
+ * dset2
+ */
+ /*
+ * Create a new dataset within the fileid1 using defined dataspace and
+ * datatype and default dataset creation properties.
+ */
+ dset2 = H5Dcreate2(grp4, GRP_R_DSETNAME2, datatype, dataspace,
+ H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
+
+ /*
+ * Write the data to the dataset using default transfer properties.
+ */
+ status = H5Dwrite(dset2, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, data2);
+
+ /*-----------------------------------------------------------------------
+ * Soft links
+ *------------------------------------------------------------------------*/
+ /*
+ * under '/' root
+ */
+ /* link to dset1 */
+ status = H5Lcreate_soft(GRP_R_DSETNAME1, fileid1, "soft_dset1", H5P_DEFAULT, H5P_DEFAULT);
+ if (status < 0)
+ {
+ fprintf(stderr, "Error: %s> H5Lcreate_soft failed.\n", GRP_RECURSE1_EXT);
+ status = FAIL;
+ goto out;
+ }
+
+ H5Dclose(dset1);
+ H5Dclose(dset2);
+ H5Gclose(grp1);
+ H5Gclose(grp2);
+ H5Gclose(grp3);
+ H5Gclose(grp4);
+
+ /*-----------------------------------------------------------------------
+ * FILE 2-3
+ *------------------------------------------------------------------------*/
+
+ /* crate target file */
+ fileid4 = H5Fcreate(GRP_RECURSE2_EXT3, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
+
+ /*-----------------------------------------------
+ * Groups
+ */
+ grp4 = H5Gcreate2(fileid4, "/g4", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
+ if (grp4 < 0)
+ {
+ fprintf(stderr, "Error: %s> H5Gcreate2 failed.\n", GRP_RECURSE2_EXT3);
+ status = FAIL;
+ goto out;
+ }
+
+ /*---------------
+ * dset2
+ */
+ /*
+ * Create a new dataset within the fileid1 using defined dataspace and
+ * datatype and default dataset creation properties.
+ */
+ dset2 = H5Dcreate2(grp4, GRP_R_DSETNAME2, datatype, dataspace,
+ H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
+
+ /*
+ * Write the data to the dataset using default transfer properties.
+ */
+ status = H5Dwrite(dset2, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, data2);
+
+ H5Gclose(grp4);
+ H5Dclose(dset2);
+
+
+ /*-----------------------------------------------------------------------
+ * FILE 2-2
+ *------------------------------------------------------------------------*/
+
+ /* crate target file */
+ fileid3 = H5Fcreate(GRP_RECURSE2_EXT2, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
+
+ /*-----------------------------------------------
+ * Groups
+ */
+ grp2 = H5Gcreate2(fileid3, "g2", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
+ if (grp2 < 0)
+ {
+ fprintf(stderr, "Error: %s> H5Gcreate2 failed.\n", GRP_RECURSE2_EXT2);
+ status = FAIL;
+ goto out;
+ }
+
+ grp3 = H5Gcreate2(grp2, "g3", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
+ if (grp3 < 0)
+ {
+ fprintf(stderr, "Error: %s> H5Gcreate2 failed.\n", GRP_RECURSE2_EXT2);
+ status = FAIL;
+ goto out;
+ }
+
+ /*---------------
+ * dset1
+ */
+ /*
+ * Create a new dataset within the fileid1 using defined dataspace and
+ * datatype and default dataset creation properties.
+ */
+ dset1 = H5Dcreate2(grp3, GRP_R_DSETNAME1, datatype, dataspace,
+ H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
+
+ /*
+ * Write the data to the dataset using default transfer properties.
+ */
+ status = H5Dwrite(dset1, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, data1);
+
+ /*-----------------------------------------------
+ * extlink to $GRP_RECURSE2_EXT3/g4
+ */
+ status = H5Lcreate_external(GRP_RECURSE2_EXT3, "/g4", fileid3, "/g2/g3/g4", H5P_DEFAULT, H5P_DEFAULT);
+ if (status < 0)
+ {
+ fprintf(stderr, "Error: %s> H5Lcreate_external failed.\n", GRP_RECURSE2_EXT2);
+ status = FAIL;
+ goto out;
+ }
+
+ H5Dclose(dset1);
+ H5Gclose(grp2);
+ H5Gclose(grp3);
+
+ /*-----------------------------------------------------------------------
+ * FILE 2-1
+ *------------------------------------------------------------------------*/
+
+ /* crate target file */
+ fileid2 = H5Fcreate(GRP_RECURSE2_EXT1, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
+
+ /*-----------------------------------------------
+ * Groups
+ */
+ grp1 = H5Gcreate2(fileid2, "g1", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
+ if (grp1 < 0)
+ {
+ fprintf(stderr, "Error: %s> H5Gcreate2 failed.\n", GRP_RECURSE1_EXT);
+ status = FAIL;
+ goto out;
+ }
+
+ /*---------------
+ * dset1
+ */
+ dset1 = H5Dcreate2(fileid2, GRP_R_DSETNAME1, datatype, dataspace,
+ H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
+
+ /*
+ * Write the data to the dataset using default transfer properties.
+ */
+ status = H5Dwrite(dset1, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, data1);
+
+ /*-----------------------------------------------------------------------
+ * Soft links
+ *------------------------------------------------------------------------*/
+ /*
+ * under '/' root
+ */
+ /* link to dset1 */
+ status = H5Lcreate_soft(GRP_R_DSETNAME1, fileid2, "soft_dset1", H5P_DEFAULT, H5P_DEFAULT);
+ if (status < 0)
+ {
+ fprintf(stderr, "Error: %s> H5Lcreate_soft failed.\n", GRP_RECURSE2_EXT1);
+ status = FAIL;
+ goto out;
+ }
+
+ /*-----------------------------------------------
+ * extlink to $GRP_RECURSE2_EXT2/g2
+ */
+ status = H5Lcreate_external(GRP_RECURSE2_EXT2, "/g2", fileid2, "/g1/g2", H5P_DEFAULT, H5P_DEFAULT);
+ if (status < 0)
+ {
+ fprintf(stderr, "Error: %s> H5Lcreate_external failed.\n", GRP_RECURSE2_EXT1);
+ status = FAIL;
+ goto out;
+ }
+
+ H5Gclose(grp1);
+ H5Dclose(dset1);
+
+out:
+ /*
+ * Close/release resources.
+ */
+ H5Sclose(dataspace);
+ H5Tclose(datatype);
+ H5Fclose(fileid1);
+ H5Fclose(fileid2);
+ H5Fclose(fileid3);
+ H5Fclose(fileid4);
+
+ return status;
+}
+
+
+/*-------------------------------------------------------------------------
+*
* Purpose: Create test files for excluding obj.
* Same structure, same obj names
* Test : exclude obj with different value to verify the rest are same
diff --git a/tools/h5diff/testfiles/h5diff_515.txt b/tools/h5diff/testfiles/h5diff_515.txt
new file mode 100644
index 0000000..077d463
--- /dev/null
+++ b/tools/h5diff/testfiles/h5diff_515.txt
@@ -0,0 +1,24 @@
+
+file1 file2
+---------------------------------------
+ x x /
+ x x /dset1
+ x x /g1
+ x x /g1/g2
+ x /g1/g2/g3
+ x /g1/g2/g3/dset1
+ x /g1/g2/g3/g4
+ x /g1/g2/g3/g4/dset2
+ x x /soft_dset1
+
+group : </> and </>
+0 differences found
+dataset: </dset1> and </dset1>
+0 differences found
+group : </g1> and </g1>
+0 differences found
+group : </g1/g2> and </g1/g2>
+0 differences found
+link : </soft_dset1> and </soft_dset1>
+0 differences found
+EXIT CODE: 1
diff --git a/tools/h5diff/testfiles/h5diff_516.txt b/tools/h5diff/testfiles/h5diff_516.txt
new file mode 100644
index 0000000..32f4bd5
--- /dev/null
+++ b/tools/h5diff/testfiles/h5diff_516.txt
@@ -0,0 +1,32 @@
+
+file1 file2
+---------------------------------------
+ x x /
+ x x /dset1
+ x x /g1
+ x x /g1/g2
+ x x /g1/g2/g3
+ x x /g1/g2/g3/dset1
+ x x /g1/g2/g3/g4
+ x x /g1/g2/g3/g4/dset2
+ x x /soft_dset1
+
+group : </> and </>
+0 differences found
+dataset: </dset1> and </dset1>
+0 differences found
+group : </g1> and </g1>
+0 differences found
+group : </g1/g2> and </g1/g2>
+0 differences found
+group : </g1/g2/g3> and </g1/g2/g3>
+0 differences found
+dataset: </g1/g2/g3/dset1> and </g1/g2/g3/dset1>
+0 differences found
+group : </g1/g2/g3/g4> and </g1/g2/g3/g4>
+0 differences found
+dataset: </g1/g2/g3/g4/dset2> and </g1/g2/g3/g4/dset2>
+0 differences found
+dataset: </soft_dset1> and </soft_dset1>
+0 differences found
+EXIT CODE: 0
diff --git a/tools/h5diff/testfiles/h5diff_517.txt b/tools/h5diff/testfiles/h5diff_517.txt
new file mode 100644
index 0000000..1b6e24f
--- /dev/null
+++ b/tools/h5diff/testfiles/h5diff_517.txt
@@ -0,0 +1,15 @@
+
+group1 group2
+---------------------------------------
+ x x
+ x x /g2
+ x /g2/g3
+ x /g2/g3/dset1
+ x /g2/g3/g4
+ x /g2/g3/g4/dset2
+
+group : </g1> and </g1>
+0 differences found
+group : </g1/g2> and </g1/g2>
+0 differences found
+EXIT CODE: 1
diff --git a/tools/h5diff/testfiles/h5diff_518.txt b/tools/h5diff/testfiles/h5diff_518.txt
new file mode 100644
index 0000000..f4761ad
--- /dev/null
+++ b/tools/h5diff/testfiles/h5diff_518.txt
@@ -0,0 +1,23 @@
+
+group1 group2
+---------------------------------------
+ x x
+ x x /g2
+ x x /g2/g3
+ x x /g2/g3/dset1
+ x x /g2/g3/g4
+ x x /g2/g3/g4/dset2
+
+group : </g1> and </g1>
+0 differences found
+group : </g1/g2> and </g1/g2>
+0 differences found
+group : </g1/g2/g3> and </g1/g2/g3>
+0 differences found
+dataset: </g1/g2/g3/dset1> and </g1/g2/g3/dset1>
+0 differences found
+group : </g1/g2/g3/g4> and </g1/g2/g3/g4>
+0 differences found
+dataset: </g1/g2/g3/g4/dset2> and </g1/g2/g3/g4/dset2>
+0 differences found
+EXIT CODE: 0
diff --git a/tools/h5diff/testfiles/h5diff_grp_recurse_ext1.h5 b/tools/h5diff/testfiles/h5diff_grp_recurse_ext1.h5
new file mode 100644
index 0000000..12a534a
--- /dev/null
+++ b/tools/h5diff/testfiles/h5diff_grp_recurse_ext1.h5
Binary files differ
diff --git a/tools/h5diff/testfiles/h5diff_grp_recurse_ext2-1.h5 b/tools/h5diff/testfiles/h5diff_grp_recurse_ext2-1.h5
new file mode 100644
index 0000000..5bb02df
--- /dev/null
+++ b/tools/h5diff/testfiles/h5diff_grp_recurse_ext2-1.h5
Binary files differ
diff --git a/tools/h5diff/testfiles/h5diff_grp_recurse_ext2-2.h5 b/tools/h5diff/testfiles/h5diff_grp_recurse_ext2-2.h5
new file mode 100644
index 0000000..312543e
--- /dev/null
+++ b/tools/h5diff/testfiles/h5diff_grp_recurse_ext2-2.h5
Binary files differ
diff --git a/tools/h5diff/testfiles/h5diff_grp_recurse_ext2-3.h5 b/tools/h5diff/testfiles/h5diff_grp_recurse_ext2-3.h5
new file mode 100644
index 0000000..43ad156
--- /dev/null
+++ b/tools/h5diff/testfiles/h5diff_grp_recurse_ext2-3.h5
Binary files differ
diff --git a/tools/h5diff/testh5diff.sh b/tools/h5diff/testh5diff.sh
index 71d78e7..710e430 100755
--- a/tools/h5diff/testh5diff.sh
+++ b/tools/h5diff/testh5diff.sh
@@ -54,8 +54,14 @@ FILE17=h5diff_ext2softlink_src.h5
FILE18=h5diff_ext2softlink_trg.h5
DANGLE_LINK_FILE1=h5diff_danglelinks1.h5
DANGLE_LINK_FILE2=h5diff_danglelinks2.h5
+/* group recursive */
GRP_RECURSE_FILE1=h5diff_grp_recurse1.h5
GRP_RECURSE_FILE2=h5diff_grp_recurse2.h5
+/* group recursive - same structure via external links through files */
+GRP_RECURSE1_EXT=h5diff_grp_recurse_ext1.h5
+GRP_RECURSE2_EXT1=h5diff_grp_recurse_ext2-1.h5
+GRP_RECURSE2_EXT2=h5diff_grp_recurse_ext2-2.h5
+GRP_RECURSE2_EXT3=h5diff_grp_recurse_ext2-3.h5
# same structure, same obj name with different value
EXCLUDE_FILE1_1=h5diff_exclude1-1.h5
EXCLUDE_FILE1_2=h5diff_exclude1-2.h5
@@ -733,6 +739,18 @@ TOOLTEST h5diff_512.txt -v --follow-symlinks $GRP_RECURSE_FILE1 $GRP_RECURSE_FIL
TOOLTEST h5diff_513.txt -v $GRP_RECURSE_FILE1 $GRP_RECURSE_FILE2 /slink_grp10 /slink_grp11
TOOLTEST h5diff_514.txt -v --follow-symlinks $GRP_RECURSE_FILE1 $GRP_RECURSE_FILE2 /slink_grp10 /slink_grp11
+###############################################################################
+# Test for group recursive diff via multi-linked external links
+# With follow-symlinks, file $GRP_RECURSE1_EXT and $GRP_RECURSE2_EXT1 should
+# be same with the external links.
+###############################################################################
+# file vs file
+TOOLTEST h5diff_515.txt -v $GRP_RECURSE1_EXT $GRP_RECURSE2_EXT1
+TOOLTEST h5diff_516.txt -v --follow-symlinks $GRP_RECURSE1_EXT $GRP_RECURSE2_EXT1
+# group vs group
+TOOLTEST h5diff_517.txt -v $GRP_RECURSE1_EXT $GRP_RECURSE2_EXT1 /g1
+TOOLTEST h5diff_518.txt -v --follow-symlinks $GRP_RECURSE1_EXT $GRP_RECURSE2_EXT1 /g1
+
# ##############################################################################
# # Exclude objects (--exclude-path)
# ##############################################################################