diff options
Diffstat (limited to 'tests')
43 files changed, 9706 insertions, 9706 deletions
diff --git a/tests/allocator.cpp b/tests/allocator.cpp index e0efeef..234f95c 100644 --- a/tests/allocator.cpp +++ b/tests/allocator.cpp @@ -1,94 +1,94 @@ -#include "allocator.hpp"
 -
 -#include <string.h>
 -
 -// Low-level allocation functions
 -#if defined(_WIN32) || defined(_WIN64)
 -#	ifdef __MWERKS__
 -#		pragma ANSI_strict off // disable ANSI strictness to include windows.h
 -#		pragma cpp_extensions on // enable some extensions to include windows.h
 -#	endif
 -
 -#	ifdef _XBOX_VER
 -#		define NOD3D
 -#		include <xtl.h>
 -#	else
 -#		include <windows.h>
 -#	endif
 -
 -namespace
 -{
 -	const size_t PAGE_SIZE = 4096;
 -
 -	void* allocate(size_t size)
 -	{
 -		size_t aligned_size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
 -
 -		void* ptr = VirtualAlloc(0, aligned_size + PAGE_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
 -		if (!ptr) return 0;
 -
 -		void* end = (char*)ptr + aligned_size;
 -
 -		DWORD old_flags;
 -		VirtualProtect(end, PAGE_SIZE, PAGE_NOACCESS, &old_flags);
 -
 -		return (char*)end - size;
 -	}
 -
 -	void deallocate(void* ptr, size_t size)
 -	{
 -		size_t aligned_size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
 -
 -		void* rptr = (char*)ptr + size - aligned_size;
 -
 -		DWORD old_flags;
 -		VirtualProtect(rptr, aligned_size + PAGE_SIZE, PAGE_NOACCESS, &old_flags);
 -	}
 -}
 -#else
 -#	include <stdlib.h>
 -
 -namespace
 -{
 -	void* allocate(size_t size)
 -	{
 -		return malloc(size);
 -	}
 -
 -	void deallocate(void* ptr, size_t size)
 -	{
 -		(void)size;
 -
 -		free(ptr);
 -	}
 -}
 -#endif
 -
 -// High-level allocation functions
 -void* memory_allocate(size_t size)
 -{
 -	void* result = allocate(size + sizeof(size_t));
 -	if (!result) return 0;
 -
 -	memcpy(result, &size, sizeof(size_t));
 -
 -	return (size_t*)result + 1;
 -}
 -
 -size_t memory_size(void* ptr)
 -{
 -	size_t result;
 -	memcpy(&result, (size_t*)ptr - 1, sizeof(size_t));
 -
 -	return result;
 -}
 -
 -void memory_deallocate(void* ptr)
 -{
 -	if (!ptr) return;
 -
 -	size_t size = memory_size(ptr);
 -
 -	deallocate((size_t*)ptr - 1, size + sizeof(size_t));
 -}
 -
 +#include "allocator.hpp" + +#include <string.h> + +// Low-level allocation functions +#if defined(_WIN32) || defined(_WIN64) +#	ifdef __MWERKS__ +#		pragma ANSI_strict off // disable ANSI strictness to include windows.h +#		pragma cpp_extensions on // enable some extensions to include windows.h +#	endif + +#	ifdef _XBOX_VER +#		define NOD3D +#		include <xtl.h> +#	else +#		include <windows.h> +#	endif + +namespace +{ +	const size_t PAGE_SIZE = 4096; + +	void* allocate(size_t size) +	{ +		size_t aligned_size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); + +		void* ptr = VirtualAlloc(0, aligned_size + PAGE_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); +		if (!ptr) return 0; + +		void* end = (char*)ptr + aligned_size; + +		DWORD old_flags; +		VirtualProtect(end, PAGE_SIZE, PAGE_NOACCESS, &old_flags); + +		return (char*)end - size; +	} + +	void deallocate(void* ptr, size_t size) +	{ +		size_t aligned_size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); + +		void* rptr = (char*)ptr + size - aligned_size; + +		DWORD old_flags; +		VirtualProtect(rptr, aligned_size + PAGE_SIZE, PAGE_NOACCESS, &old_flags); +	} +} +#else +#	include <stdlib.h> + +namespace +{ +	void* allocate(size_t size) +	{ +		return malloc(size); +	} + +	void deallocate(void* ptr, size_t size) +	{ +		(void)size; + +		free(ptr); +	} +} +#endif + +// High-level allocation functions +void* memory_allocate(size_t size) +{ +	void* result = allocate(size + sizeof(size_t)); +	if (!result) return 0; + +	memcpy(result, &size, sizeof(size_t)); + +	return (size_t*)result + 1; +} + +size_t memory_size(void* ptr) +{ +	size_t result; +	memcpy(&result, (size_t*)ptr - 1, sizeof(size_t)); + +	return result; +} + +void memory_deallocate(void* ptr) +{ +	if (!ptr) return; + +	size_t size = memory_size(ptr); + +	deallocate((size_t*)ptr - 1, size + sizeof(size_t)); +} + diff --git a/tests/allocator.hpp b/tests/allocator.hpp index 677fbe4..cb52c91 100644 --- a/tests/allocator.hpp +++ b/tests/allocator.hpp @@ -1,10 +1,10 @@ -#ifndef HEADER_TEST_ALLOCATOR_HPP
 -#define HEADER_TEST_ALLOCATOR_HPP
 -
 -#include <stddef.h>
 -
 -void* memory_allocate(size_t size);
 -size_t memory_size(void* ptr);
 -void memory_deallocate(void* ptr);
 -
 -#endif
 +#ifndef HEADER_TEST_ALLOCATOR_HPP +#define HEADER_TEST_ALLOCATOR_HPP + +#include <stddef.h> + +void* memory_allocate(size_t size); +size_t memory_size(void* ptr); +void memory_deallocate(void* ptr); + +#endif diff --git a/tests/archive.pl b/tests/archive.pl index 240dd02..4ede302 100644 --- a/tests/archive.pl +++ b/tests/archive.pl @@ -1,60 +1,60 @@ -#!/usr/bin/perl
 -
 -use Archive::Tar;
 -use Archive::Zip;
 -
 -my $target = shift @ARGV;
 -my @sources = @ARGV;
 -
 -my $zip = $target =~ /\.zip$/;
 -
 -my $arch = $zip ? Archive::Zip->new : Archive::Tar->new;
 -
 -for $source (sort {$a cmp $b} @sources)
 -{
 -	my $contents = &readfile_contents($source);
 -	my $meta = &readfile_meta($source);
 -
 -	if ($zip)
 -	{
 -		my $path = $source;
 -		$arch->addDirectory($path) if $path =~ s/\/[^\/]+$/\// && !defined($arch->memberNamed($path));
 -
 -		my $member = $arch->addString($contents, $source);
 -
 -		$member->desiredCompressionMethod(COMPRESSION_DEFLATED);
 -		$member->desiredCompressionLevel(9);
 -
 -		$member->setLastModFileDateTimeFromUnix($$meta{mtime});
 -	}
 -	else
 -	{
 -		# tgz releases are for Unix people, Unix people like Unix newlines
 -		$contents =~ s/\r//g if (-T $source);
 -
 -		$arch->add_data($source, $contents, $meta);
 -	}
 -}
 -
 -$zip ? $arch->overwriteAs($target) : $arch->write($target, 9);
 -
 -sub readfile_contents
 -{
 -	my $file = shift;
 -
 -	open FILE, $file or die "Can't open $file: $!";
 -	binmode FILE;
 -	my @contents = <FILE>;
 -	close FILE;
 -
 -	return join('', @contents);
 -}
 -
 -sub readfile_meta
 -{
 -	my $file = shift;
 -
 -    my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($file);
 -
 -	return {mtime => $mtime};
 -}
 +#!/usr/bin/perl + +use Archive::Tar; +use Archive::Zip; + +my $target = shift @ARGV; +my @sources = @ARGV; + +my $zip = $target =~ /\.zip$/; + +my $arch = $zip ? Archive::Zip->new : Archive::Tar->new; + +for $source (sort {$a cmp $b} @sources) +{ +	my $contents = &readfile_contents($source); +	my $meta = &readfile_meta($source); + +	if ($zip) +	{ +		my $path = $source; +		$arch->addDirectory($path) if $path =~ s/\/[^\/]+$/\// && !defined($arch->memberNamed($path)); + +		my $member = $arch->addString($contents, $source); + +		$member->desiredCompressionMethod(COMPRESSION_DEFLATED); +		$member->desiredCompressionLevel(9); + +		$member->setLastModFileDateTimeFromUnix($$meta{mtime}); +	} +	else +	{ +		# tgz releases are for Unix people, Unix people like Unix newlines +		$contents =~ s/\r//g if (-T $source); + +		$arch->add_data($source, $contents, $meta); +	} +} + +$zip ? $arch->overwriteAs($target) : $arch->write($target, 9); + +sub readfile_contents +{ +	my $file = shift; + +	open FILE, $file or die "Can't open $file: $!"; +	binmode FILE; +	my @contents = <FILE>; +	close FILE; + +	return join('', @contents); +} + +sub readfile_meta +{ +	my $file = shift; + +    my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($file); + +	return {mtime => $mtime}; +} diff --git a/tests/autotest-local.pl b/tests/autotest-local.pl index 86e5114..b0e9a6c 100644 --- a/tests/autotest-local.pl +++ b/tests/autotest-local.pl @@ -1,126 +1,126 @@ -#!/usr/bin/perl
 -
 -use Config;
 -
 -sub permute
 -{
 -	my @defines = @_;
 -	my @result = ('');
 -	
 -	foreach $define (@defines)
 -	{
 -		push @result, map { length($_) == 0 ? $define : "$_,$define" } @result;
 -	}
 -
 -	@result;
 -}
 -
 -sub gcctoolset
 -{
 -	my $gccversion = `gcc -dumpversion`;
 -	chomp($gccversion);
 -
 -	my $gcc = "gcc$gccversion";
 -
 -	return ($^O =~ /darwin/) ? ($gcc, "${gcc}_x64", "${gcc}_ppc") : (`uname -m` =~ /64/) ? ("${gcc}_x64") : ($gcc);
 -}
 -
 -$fast = (shift eq 'fast');
 -@toolsets = ($^O =~ /MSWin/) ? (bcc, cw, dmc, ic8, ic9, ic9_x64, ic10, ic10_x64, ic11, ic11_x64, mingw34, mingw44, mingw45, mingw45_0x, mingw46_x64, msvc6, msvc7, msvc71, msvc8, msvc8_x64, msvc9, msvc9_x64, msvc10, msvc10_x64, xbox360, ps3_gcc, ps3_snc) : ($^O =~ /solaris/) ? (suncc, suncc_x64) : &gcctoolset();
 -@configurations = (debug, release);
 -@defines = (PUGIXML_NO_XPATH, PUGIXML_NO_EXCEPTIONS, PUGIXML_NO_STL, PUGIXML_WCHAR_MODE);
 -$stddefine = 'PUGIXML_STANDARD';
 -
 -if ($fast)
 -{
 -	@defines = (PUGIXML_WCHAR_MODE);
 -	@configurations = (debug);
 -}
 -
 -@definesets = permute(@defines);
 -
 -print "### autotest begin " . scalar localtime() . "\n";
 -
 -# print SVN revision info
 -print "### autotest revision $1\n" if (`svn info` =~ /Revision:\s+(\d+)/);
 -
 -# build all configurations
 -%results = ();
 -
 -foreach $toolset (@toolsets)
 -{
 -	my $cmdline = "jam";
 -
 -	# parallel build on non-windows platforms (since jam can't detect processor count)
 -	$cmdline .= " -j6" if ($^O !~ /MSWin/);
 -	
 -	# add toolset
 -	$cmdline .= " toolset=$toolset";
 -
 -	# add configurations
 -	$cmdline .= " configuration=" . join(',', @configurations);
 -
 -	# add definesets
 -	$cmdline .= " defines=$stddefine";
 -
 -	foreach $defineset (@definesets)
 -	{
 -		if ($defineset !~ /NO_XPATH/ && $defineset =~ /NO_EXCEPTIONS/) { next; }
 -		if ($defineset !~ /NO_XPATH/ && $defineset =~ /NO_STL/) { next; }
 -
 -		$cmdline .= ":$defineset" if ($defineset ne '');
 -
 -		# any configuration with prepare but without result is treated as failed
 -		foreach $configuration (@configurations)
 -		{
 -			print "### autotest $Config{archname} $toolset $configuration [$defineset] prepare\n";
 -		}
 -	}
 -
 -	print STDERR "*** testing $toolset... ***\n";
 -
 -	# launch command
 -	print "### autotest launch $cmdline\n";
 -
 -	open PIPE, "$cmdline autotest=on coverage |" || die "$cmdline failed: $!\n";
 -
 -	# parse build output
 -	while (<PIPE>)
 -	{
 -		# ... autotest release [wchar] success
 -		if (/^\.\.\. autotest (\S+) \[(.*?)\] success/)
 -		{
 -			my $configuration = $1;
 -			my $defineset = ($2 eq $stddefine) ? '' : $2;
 -
 -			print "### autotest $Config{archname} $toolset $configuration [$defineset] success\n";
 -		}
 -		# ... autotest release [wchar] gcov
 -		elsif (/^\.\.\. autotest (\S+) \[(.*?)\] gcov/)
 -		{
 -			my $configuration = $1;
 -			my $defineset = ($2 eq $stddefine) ? '' : $2;
 -			my $file;
 -
 -			$file = "pugixml $1" if (/pugixml\.cpp' executed:([^%]+)%/);
 -			$file = "pugixpath $1" if (/pugixpath\.cpp' executed:([^%]+)%/);
 -
 -			if (defined($file))
 -			{
 -				print "### autotest $Config{archname} $toolset $configuration [$defineset] coverage $file\n";
 -			}
 -			else
 -			{
 -				print;
 -			}
 -		}
 -		else
 -		{
 -			print;
 -		}
 -	}
 -
 -	close PIPE;
 -}
 -
 -print "### autotest end " . scalar localtime() . "\n";
 +#!/usr/bin/perl + +use Config; + +sub permute +{ +	my @defines = @_; +	my @result = (''); +	 +	foreach $define (@defines) +	{ +		push @result, map { length($_) == 0 ? $define : "$_,$define" } @result; +	} + +	@result; +} + +sub gcctoolset +{ +	my $gccversion = `gcc -dumpversion`; +	chomp($gccversion); + +	my $gcc = "gcc$gccversion"; + +	return ($^O =~ /darwin/) ? ($gcc, "${gcc}_x64", "${gcc}_ppc") : (`uname -m` =~ /64/) ? ("${gcc}_x64") : ($gcc); +} + +$fast = (shift eq 'fast'); +@toolsets = ($^O =~ /MSWin/) ? (bcc, cw, dmc, ic8, ic9, ic9_x64, ic10, ic10_x64, ic11, ic11_x64, mingw34, mingw44, mingw45, mingw45_0x, mingw46_x64, msvc6, msvc7, msvc71, msvc8, msvc8_x64, msvc9, msvc9_x64, msvc10, msvc10_x64, xbox360, ps3_gcc, ps3_snc) : ($^O =~ /solaris/) ? (suncc, suncc_x64) : &gcctoolset(); +@configurations = (debug, release); +@defines = (PUGIXML_NO_XPATH, PUGIXML_NO_EXCEPTIONS, PUGIXML_NO_STL, PUGIXML_WCHAR_MODE); +$stddefine = 'PUGIXML_STANDARD'; + +if ($fast) +{ +	@defines = (PUGIXML_WCHAR_MODE); +	@configurations = (debug); +} + +@definesets = permute(@defines); + +print "### autotest begin " . scalar localtime() . "\n"; + +# print SVN revision info +print "### autotest revision $1\n" if (`svn info` =~ /Revision:\s+(\d+)/); + +# build all configurations +%results = (); + +foreach $toolset (@toolsets) +{ +	my $cmdline = "jam"; + +	# parallel build on non-windows platforms (since jam can't detect processor count) +	$cmdline .= " -j6" if ($^O !~ /MSWin/); +	 +	# add toolset +	$cmdline .= " toolset=$toolset"; + +	# add configurations +	$cmdline .= " configuration=" . join(',', @configurations); + +	# add definesets +	$cmdline .= " defines=$stddefine"; + +	foreach $defineset (@definesets) +	{ +		if ($defineset !~ /NO_XPATH/ && $defineset =~ /NO_EXCEPTIONS/) { next; } +		if ($defineset !~ /NO_XPATH/ && $defineset =~ /NO_STL/) { next; } + +		$cmdline .= ":$defineset" if ($defineset ne ''); + +		# any configuration with prepare but without result is treated as failed +		foreach $configuration (@configurations) +		{ +			print "### autotest $Config{archname} $toolset $configuration [$defineset] prepare\n"; +		} +	} + +	print STDERR "*** testing $toolset... ***\n"; + +	# launch command +	print "### autotest launch $cmdline\n"; + +	open PIPE, "$cmdline autotest=on coverage |" || die "$cmdline failed: $!\n"; + +	# parse build output +	while (<PIPE>) +	{ +		# ... autotest release [wchar] success +		if (/^\.\.\. autotest (\S+) \[(.*?)\] success/) +		{ +			my $configuration = $1; +			my $defineset = ($2 eq $stddefine) ? '' : $2; + +			print "### autotest $Config{archname} $toolset $configuration [$defineset] success\n"; +		} +		# ... autotest release [wchar] gcov +		elsif (/^\.\.\. autotest (\S+) \[(.*?)\] gcov/) +		{ +			my $configuration = $1; +			my $defineset = ($2 eq $stddefine) ? '' : $2; +			my $file; + +			$file = "pugixml $1" if (/pugixml\.cpp' executed:([^%]+)%/); +			$file = "pugixpath $1" if (/pugixpath\.cpp' executed:([^%]+)%/); + +			if (defined($file)) +			{ +				print "### autotest $Config{archname} $toolset $configuration [$defineset] coverage $file\n"; +			} +			else +			{ +				print; +			} +		} +		else +		{ +			print; +		} +	} + +	close PIPE; +} + +print "### autotest end " . scalar localtime() . "\n"; diff --git a/tests/autotest-report.pl b/tests/autotest-report.pl index a01a907..993674a 100644 --- a/tests/autotest-report.pl +++ b/tests/autotest-report.pl @@ -1,199 +1,199 @@ -#!/usr/bin/perl
 -
 -# pretty-printing
 -sub prettysuffix
 -{
 -	my $suffix = shift;
 -
 -	return " C++0x" if ($suffix eq '_0x');
 -	return " x64" if ($suffix eq '_x64');
 -	return " PPC" if ($suffix eq '_ppc');
 -
 -	return "";
 -}
 -
 -sub prettytoolset
 -{
 -	my $toolset = shift;
 -
 -	return "Borland C++ 5.82" if ($toolset eq 'bcc');
 -	return "Metrowerks CodeWarrior 8" if ($toolset eq 'cw');
 -	return "Digital Mars C++ 8.51" if ($toolset eq 'dmc');
 -	return "Sun C++ 5.10" . prettysuffix($1) if ($toolset =~ /^suncc(.*)$/);
 -
 -	return "Intel C++ Compiler $1.0" . prettysuffix($2) if ($toolset =~ /^ic(\d+)(.*)$/);
 -	return "MinGW (GCC $1.$2)" . prettysuffix($3) if ($toolset =~ /^mingw(\d)(\d)(.*)$/);
 -	return "Microsoft Visual C++ 7.1" if ($toolset eq 'msvc71');
 -	return "Microsoft Visual C++ $1.0" . prettysuffix($2) if ($toolset =~ /^msvc(\d+)(.*)$/);
 -	return "GNU C++ Compiler $1" . prettysuffix($2) if ($toolset =~ /^gcc([\d.]*)(.*)$/);
 -
 -	return "Microsoft Xbox360 Compiler" if ($toolset =~ /^xbox360/);
 -	return "Sony PlayStation3 GCC" if ($toolset =~ /^ps3_gcc/);
 -	return "Sony PlayStation3 SNC" if ($toolset =~ /^ps3_snc/);
 -
 -	$toolset;
 -}
 -
 -sub prettyplatform
 -{
 -	my ($platform, $toolset) = @_;
 -
 -	return "solaris" if ($platform =~ /solaris/);
 -
 -	return "macos" if ($platform =~ /darwin/);
 -
 -	return "linux64" if ($platform =~ /64-linux/);
 -	return "linux32" if ($platform =~ /86-linux/);
 -
 -	return "fbsd64" if ($platform =~ /64-freebsd/);
 -	return "fbsd32" if ($platform =~ /86-freebsd/);
 -
 -	return "x360" if ($toolset =~ /^xbox360/);
 -	return "ps3" if ($toolset =~ /^ps3/);
 -
 -	return "win64" if ($platform =~ /MSWin32-x64/);
 -	return "win32" if ($platform =~ /MSWin32/);
 -
 -	$platform;
 -}
 -
 -# parse build log
 -%results = ();
 -%toolsets = ();
 -%defines = ();
 -%configurations = ();
 -
 -sub insertindex
 -{
 -	my ($hash, $key) = @_;
 -
 -	$$hash{$key} = scalar(keys %$hash) unless defined $$hash{$key};
 -}
 -
 -while (<>)
 -{
 -	### autotest i386-freebsd-64int gcc release [wchar] result 0 97.78 98.85
 -	if (/^### autotest (\S+) (\S+) (\S+) \[(.*?)\] (.*)/)
 -	{
 -		my ($platform, $toolset, $configuration, $defineset, $info) = ($1, $2, $3, $4, $5);
 -
 -		my $fulltool = &prettyplatform($platform, $toolset) . ' ' . &prettytoolset($toolset);
 -		my $fullconf = "$configuration $defineset";
 -
 -		if ($info =~ /^prepare/)
 -		{
 -			$results{$fulltool}{$fullconf}{result} = 1;
 -		}
 -		elsif ($info =~ /^success/)
 -		{
 -			$results{$fulltool}{$fullconf}{result} = 0;
 -		}
 -		elsif ($info =~ /^coverage (\S+) (\S+)/)
 -		{
 -			$results{$fulltool}{$fullconf}{"coverage_$1"} = $2;
 -		}
 -		else
 -		{
 -			print STDERR "Unrecognized autotest infoline $_";
 -		}
 -
 -		&insertindex(\%toolsets, $fulltool);
 -
 -		$defines{$_} = 1 foreach (split /,/, $defineset);
 -		&insertindex(\%configurations, $fullconf);
 -	}
 -	elsif (/^### autotest revision (\d+)/)
 -	{
 -		if (defined $revision && $revision != $1)
 -		{
 -			print STDERR "Autotest build report contains several revisions: $revision, $1\n";
 -		}
 -		else
 -		{
 -			$revision = $1;
 -		}
 -	}
 -}
 -
 -# make arrays of toolsets and configurations
 -@toolsetarray = ();
 -@configurationarray = ();
 -
 -$toolsetarray[$toolsets{$_}] = $_ foreach (keys %toolsets);
 -$configurationarray[$configurations{$_}] = $_ foreach (keys %configurations);
 -
 -# print header
 -$stylesheet = <<END;
 -table.autotest { border: 1px solid black; border-left: none; border-top: none; }
 -table.autotest td { border: 1px solid black; border-right: none; border-bottom: none; }
 -END
 -
 -print <<END;
 -<html><head><title>pugixml autotest report</title><style type="text/css"><!-- $stylesheet --></style></head><body>
 -<h3>pugixml autotest report</h3>
 -<table border=1 cellspacing=0 cellpadding=4 class="autotest">
 -END
 -
 -# print configuration header (release/debug)
 -print "<tr><td align='right' colspan=2>configuration</td>";
 -print "<td>".(split /\s+/)[0]."</td>" foreach (@configurationarray);
 -print "</tr>\n";
 -
 -# print defines header (one row for each define)
 -foreach $define (sort {$a cmp $b} keys %defines)
 -{
 -	print "<tr><td align='right' colspan=2><small>$define</small></td>";
 -
 -	foreach (@configurationarray)
 -	{
 -		my $present = ($_ =~ /\b$define\b/);
 -		my $color = $present ? "#cccccc" : "#ffffff";
 -		print "<td bgcolor='$color' align='center'>" . ($present ? "+" : " ") . "</td>";
 -	}
 -	print "</tr>\n";
 -}
 -
 -# print data (one row for each toolset)
 -foreach $tool (@toolsetarray)
 -{
 -	my ($platform, $toolset) = split(/\s+/, $tool, 2);
 -	print "<tr><td style='border-right: none' align='center'><small>$platform</small></td><td style='border-left: none'>$toolset</td>";
 -
 -	foreach (@configurationarray)
 -	{
 -		my $info = $results{$tool}{$_};
 -
 -		if (!defined $$info{result})
 -		{
 -			print "<td bgcolor='#cccccc'> </td>";
 -		}
 -		elsif ($$info{result} == 0)
 -		{
 -			my ($coverage_pugixml, $coverage_pugixpath) = ($$info{coverage_pugixml}, $$info{coverage_pugixpath});
 -
 -			print "<td bgcolor='#00ff00' align='center'>pass";
 -				
 -			if ($coverage_pugixml > 0 || $coverage_pugixpath > 0)
 -			{
 -				print "<br><font size='-2'>" . ($coverage_pugixml + 0) . "%<br>" . ($coverage_pugixpath + 0) . "%</font>";
 -			}
 -
 -			print "</td>";
 -		}
 -		else
 -		{
 -			print "<td bgcolor='#ff0000' align='center'>fail</td>"
 -		}
 -	}
 -
 -	print "</tr>\n";
 -}
 -
 -# print footer
 -$date = localtime;
 -
 -print <<END;
 -</table><br>
 -Generated on $date from Subversion r$revision
 -</body></html>
 -END
 +#!/usr/bin/perl + +# pretty-printing +sub prettysuffix +{ +	my $suffix = shift; + +	return " C++0x" if ($suffix eq '_0x'); +	return " x64" if ($suffix eq '_x64'); +	return " PPC" if ($suffix eq '_ppc'); + +	return ""; +} + +sub prettytoolset +{ +	my $toolset = shift; + +	return "Borland C++ 5.82" if ($toolset eq 'bcc'); +	return "Metrowerks CodeWarrior 8" if ($toolset eq 'cw'); +	return "Digital Mars C++ 8.51" if ($toolset eq 'dmc'); +	return "Sun C++ 5.10" . prettysuffix($1) if ($toolset =~ /^suncc(.*)$/); + +	return "Intel C++ Compiler $1.0" . prettysuffix($2) if ($toolset =~ /^ic(\d+)(.*)$/); +	return "MinGW (GCC $1.$2)" . prettysuffix($3) if ($toolset =~ /^mingw(\d)(\d)(.*)$/); +	return "Microsoft Visual C++ 7.1" if ($toolset eq 'msvc71'); +	return "Microsoft Visual C++ $1.0" . prettysuffix($2) if ($toolset =~ /^msvc(\d+)(.*)$/); +	return "GNU C++ Compiler $1" . prettysuffix($2) if ($toolset =~ /^gcc([\d.]*)(.*)$/); + +	return "Microsoft Xbox360 Compiler" if ($toolset =~ /^xbox360/); +	return "Sony PlayStation3 GCC" if ($toolset =~ /^ps3_gcc/); +	return "Sony PlayStation3 SNC" if ($toolset =~ /^ps3_snc/); + +	$toolset; +} + +sub prettyplatform +{ +	my ($platform, $toolset) = @_; + +	return "solaris" if ($platform =~ /solaris/); + +	return "macos" if ($platform =~ /darwin/); + +	return "linux64" if ($platform =~ /64-linux/); +	return "linux32" if ($platform =~ /86-linux/); + +	return "fbsd64" if ($platform =~ /64-freebsd/); +	return "fbsd32" if ($platform =~ /86-freebsd/); + +	return "x360" if ($toolset =~ /^xbox360/); +	return "ps3" if ($toolset =~ /^ps3/); + +	return "win64" if ($platform =~ /MSWin32-x64/); +	return "win32" if ($platform =~ /MSWin32/); + +	$platform; +} + +# parse build log +%results = (); +%toolsets = (); +%defines = (); +%configurations = (); + +sub insertindex +{ +	my ($hash, $key) = @_; + +	$$hash{$key} = scalar(keys %$hash) unless defined $$hash{$key}; +} + +while (<>) +{ +	### autotest i386-freebsd-64int gcc release [wchar] result 0 97.78 98.85 +	if (/^### autotest (\S+) (\S+) (\S+) \[(.*?)\] (.*)/) +	{ +		my ($platform, $toolset, $configuration, $defineset, $info) = ($1, $2, $3, $4, $5); + +		my $fulltool = &prettyplatform($platform, $toolset) . ' ' . &prettytoolset($toolset); +		my $fullconf = "$configuration $defineset"; + +		if ($info =~ /^prepare/) +		{ +			$results{$fulltool}{$fullconf}{result} = 1; +		} +		elsif ($info =~ /^success/) +		{ +			$results{$fulltool}{$fullconf}{result} = 0; +		} +		elsif ($info =~ /^coverage (\S+) (\S+)/) +		{ +			$results{$fulltool}{$fullconf}{"coverage_$1"} = $2; +		} +		else +		{ +			print STDERR "Unrecognized autotest infoline $_"; +		} + +		&insertindex(\%toolsets, $fulltool); + +		$defines{$_} = 1 foreach (split /,/, $defineset); +		&insertindex(\%configurations, $fullconf); +	} +	elsif (/^### autotest revision (\d+)/) +	{ +		if (defined $revision && $revision != $1) +		{ +			print STDERR "Autotest build report contains several revisions: $revision, $1\n"; +		} +		else +		{ +			$revision = $1; +		} +	} +} + +# make arrays of toolsets and configurations +@toolsetarray = (); +@configurationarray = (); + +$toolsetarray[$toolsets{$_}] = $_ foreach (keys %toolsets); +$configurationarray[$configurations{$_}] = $_ foreach (keys %configurations); + +# print header +$stylesheet = <<END; +table.autotest { border: 1px solid black; border-left: none; border-top: none; } +table.autotest td { border: 1px solid black; border-right: none; border-bottom: none; } +END + +print <<END; +<html><head><title>pugixml autotest report</title><style type="text/css"><!-- $stylesheet --></style></head><body> +<h3>pugixml autotest report</h3> +<table border=1 cellspacing=0 cellpadding=4 class="autotest"> +END + +# print configuration header (release/debug) +print "<tr><td align='right' colspan=2>configuration</td>"; +print "<td>".(split /\s+/)[0]."</td>" foreach (@configurationarray); +print "</tr>\n"; + +# print defines header (one row for each define) +foreach $define (sort {$a cmp $b} keys %defines) +{ +	print "<tr><td align='right' colspan=2><small>$define</small></td>"; + +	foreach (@configurationarray) +	{ +		my $present = ($_ =~ /\b$define\b/); +		my $color = $present ? "#cccccc" : "#ffffff"; +		print "<td bgcolor='$color' align='center'>" . ($present ? "+" : " ") . "</td>"; +	} +	print "</tr>\n"; +} + +# print data (one row for each toolset) +foreach $tool (@toolsetarray) +{ +	my ($platform, $toolset) = split(/\s+/, $tool, 2); +	print "<tr><td style='border-right: none' align='center'><small>$platform</small></td><td style='border-left: none'>$toolset</td>"; + +	foreach (@configurationarray) +	{ +		my $info = $results{$tool}{$_}; + +		if (!defined $$info{result}) +		{ +			print "<td bgcolor='#cccccc'> </td>"; +		} +		elsif ($$info{result} == 0) +		{ +			my ($coverage_pugixml, $coverage_pugixpath) = ($$info{coverage_pugixml}, $$info{coverage_pugixpath}); + +			print "<td bgcolor='#00ff00' align='center'>pass"; +				 +			if ($coverage_pugixml > 0 || $coverage_pugixpath > 0) +			{ +				print "<br><font size='-2'>" . ($coverage_pugixml + 0) . "%<br>" . ($coverage_pugixpath + 0) . "%</font>"; +			} + +			print "</td>"; +		} +		else +		{ +			print "<td bgcolor='#ff0000' align='center'>fail</td>" +		} +	} + +	print "</tr>\n"; +} + +# print footer +$date = localtime; + +print <<END; +</table><br> +Generated on $date from Subversion r$revision +</body></html> +END diff --git a/tests/common.hpp b/tests/common.hpp index b466c09..35e4717 100644 --- a/tests/common.hpp +++ b/tests/common.hpp @@ -1,8 +1,8 @@ -#ifndef HEADER_TEST_COMMON_HPP
 -#define HEADER_TEST_COMMON_HPP
 -
 -#include "test.hpp"
 -
 -using namespace pugi;
 -
 -#endif
 +#ifndef HEADER_TEST_COMMON_HPP +#define HEADER_TEST_COMMON_HPP + +#include "test.hpp" + +using namespace pugi; + +#endif diff --git a/tests/data/multiline.xml b/tests/data/multiline.xml index 3607e7f..0f0fe3c 100644 --- a/tests/data/multiline.xml +++ b/tests/data/multiline.xml @@ -1,3 +1,3 @@ -<node1 />
 -<node2 />
 -<node3 />
 +<node1 /> +<node2 /> +<node3 /> diff --git a/tests/gcov-filter.pl b/tests/gcov-filter.pl index 8cbccc5..c68aa1f 100644 --- a/tests/gcov-filter.pl +++ b/tests/gcov-filter.pl @@ -1,13 +1,13 @@ -#!/usr/bin/perl
 -
 -$prefix = join(' ', @ARGV);
 -$prefix .= ' ' if ($prefix ne '');
 -
 -$lines = join('', <STDIN>);
 -$lines =~ s/File (.+)\nLines (.+)\n(.+\n)*\n/$1 $2\n/g;
 -$lines =~ s/.+include\/c\+\+.+\n//g;
 -
 -foreach $line (split /\n/, $lines)
 -{
 -	print "$prefix$line\n";
 -}
 +#!/usr/bin/perl + +$prefix = join(' ', @ARGV); +$prefix .= ' ' if ($prefix ne ''); + +$lines = join('', <STDIN>); +$lines =~ s/File (.+)\nLines (.+)\n(.+\n)*\n/$1 $2\n/g; +$lines =~ s/.+include\/c\+\+.+\n//g; + +foreach $line (split /\n/, $lines) +{ +	print "$prefix$line\n"; +} diff --git a/tests/helpers.hpp b/tests/helpers.hpp index b160a85..abe6626 100644 --- a/tests/helpers.hpp +++ b/tests/helpers.hpp @@ -1,97 +1,97 @@ -#ifndef HEADER_TEST_HELPERS_HPP
 -#define HEADER_TEST_HELPERS_HPP
 -
 -#include "common.hpp"
 -
 -#include <utility>
 -
 -template <typename T> static void generic_bool_ops_test(const T& obj)
 -{
 -	T null;
 -
 -	CHECK(!null);
 -	CHECK(obj);
 -	CHECK(!!obj);
 -
 -	bool b1 = null, b2 = obj;
 -
 -	CHECK(!b1);
 -	CHECK(b2);
 -
 -	CHECK(obj && b2);
 -	CHECK(obj || b2);
 -	CHECK(obj && obj);
 -	CHECK(obj || obj);
 -}
 -
 -template <typename T> static void generic_eq_ops_test(const T& obj1, const T& obj2)
 -{
 -	T null = T();
 -
 -	// operator==
 -	CHECK(null == null);
 -	CHECK(obj1 == obj1);
 -	CHECK(!(null == obj1));
 -	CHECK(!(null == obj2));
 -	CHECK(T(null) == null);
 -	CHECK(T(obj1) == obj1);
 -
 -	// operator!=
 -	CHECK(!(null != null));
 -	CHECK(!(obj1 != obj1));
 -	CHECK(null != obj1);
 -	CHECK(null != obj2);
 -	CHECK(!(T(null) != null));
 -	CHECK(!(T(obj1) != obj1));
 -}
 -
 -template <typename T> static void generic_rel_ops_test(T obj1, T obj2)
 -{
 -	T null = T();
 -
 -	// obj1 < obj2 (we use operator<, but there is no other choice
 -	if (obj1 > obj2)
 -	{
 -		T temp = obj1;
 -		obj1 = obj2;
 -		obj2 = temp;
 -	}
 -
 -	// operator<
 -	CHECK(null < obj1);
 -	CHECK(null < obj2);
 -	CHECK(obj1 < obj2);
 -	CHECK(!(null < null));
 -	CHECK(!(obj1 < obj1));
 -	CHECK(!(obj1 < null));
 -	CHECK(!(obj2 < obj1));
 -
 -	// operator<=
 -	CHECK(null <= obj1);
 -	CHECK(null <= obj2);
 -	CHECK(obj1 <= obj2);
 -	CHECK(null <= null);
 -	CHECK(obj1 <= obj1);
 -	CHECK(!(obj1 <= null));
 -	CHECK(!(obj2 <= obj1));
 -
 -	// operator>
 -	CHECK(obj1 > null);
 -	CHECK(obj2 > null);
 -	CHECK(obj2 > obj1);
 -	CHECK(!(null > null));
 -	CHECK(!(obj1 > obj1));
 -	CHECK(!(null > obj1));
 -	CHECK(!(obj1 > obj2));
 -
 -	// operator>=
 -	CHECK(obj1 >= null);
 -	CHECK(obj2 >= null);
 -	CHECK(obj2 >= obj1);
 -	CHECK(null >= null);
 -	CHECK(obj1 >= obj1);
 -	CHECK(!(null >= obj1));
 -	CHECK(!(obj1 >= obj2));
 -}
 -
 -#endif
 +#ifndef HEADER_TEST_HELPERS_HPP +#define HEADER_TEST_HELPERS_HPP + +#include "common.hpp" + +#include <utility> + +template <typename T> static void generic_bool_ops_test(const T& obj) +{ +	T null; + +	CHECK(!null); +	CHECK(obj); +	CHECK(!!obj); + +	bool b1 = null, b2 = obj; + +	CHECK(!b1); +	CHECK(b2); + +	CHECK(obj && b2); +	CHECK(obj || b2); +	CHECK(obj && obj); +	CHECK(obj || obj); +} + +template <typename T> static void generic_eq_ops_test(const T& obj1, const T& obj2) +{ +	T null = T(); + +	// operator== +	CHECK(null == null); +	CHECK(obj1 == obj1); +	CHECK(!(null == obj1)); +	CHECK(!(null == obj2)); +	CHECK(T(null) == null); +	CHECK(T(obj1) == obj1); + +	// operator!= +	CHECK(!(null != null)); +	CHECK(!(obj1 != obj1)); +	CHECK(null != obj1); +	CHECK(null != obj2); +	CHECK(!(T(null) != null)); +	CHECK(!(T(obj1) != obj1)); +} + +template <typename T> static void generic_rel_ops_test(T obj1, T obj2) +{ +	T null = T(); + +	// obj1 < obj2 (we use operator<, but there is no other choice +	if (obj1 > obj2) +	{ +		T temp = obj1; +		obj1 = obj2; +		obj2 = temp; +	} + +	// operator< +	CHECK(null < obj1); +	CHECK(null < obj2); +	CHECK(obj1 < obj2); +	CHECK(!(null < null)); +	CHECK(!(obj1 < obj1)); +	CHECK(!(obj1 < null)); +	CHECK(!(obj2 < obj1)); + +	// operator<= +	CHECK(null <= obj1); +	CHECK(null <= obj2); +	CHECK(obj1 <= obj2); +	CHECK(null <= null); +	CHECK(obj1 <= obj1); +	CHECK(!(obj1 <= null)); +	CHECK(!(obj2 <= obj1)); + +	// operator> +	CHECK(obj1 > null); +	CHECK(obj2 > null); +	CHECK(obj2 > obj1); +	CHECK(!(null > null)); +	CHECK(!(obj1 > obj1)); +	CHECK(!(null > obj1)); +	CHECK(!(obj1 > obj2)); + +	// operator>= +	CHECK(obj1 >= null); +	CHECK(obj2 >= null); +	CHECK(obj2 >= obj1); +	CHECK(null >= null); +	CHECK(obj1 >= obj1); +	CHECK(!(null >= obj1)); +	CHECK(!(obj1 >= obj2)); +} + +#endif diff --git a/tests/main.cpp b/tests/main.cpp index 4330009..021c253 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -1,149 +1,149 @@ -#include "test.hpp"
 -#include "allocator.hpp"
 -
 -#include <exception>
 -#include <stdio.h>
 -#include <float.h>
 -
 -test_runner* test_runner::_tests = 0;
 -size_t test_runner::_memory_fail_threshold = 0;
 -jmp_buf test_runner::_failure_buffer;
 -const char* test_runner::_failure_message;
 -
 -static size_t g_memory_total_size = 0;
 -static size_t g_memory_total_count = 0;
 -
 -static void* custom_allocate(size_t size)
 -{
 -	if (test_runner::_memory_fail_threshold > 0 && test_runner::_memory_fail_threshold < g_memory_total_size + size)
 -		return 0;
 -	else
 -	{
 -		void* ptr = memory_allocate(size);
 -
 -		g_memory_total_size += memory_size(ptr);
 -		g_memory_total_count++;
 -		
 -		return ptr;
 -	}
 -}
 -
 -static void custom_deallocate(void* ptr)
 -{
 -	if (ptr)
 -	{
 -		g_memory_total_size -= memory_size(ptr);
 -		g_memory_total_count--;
 -		
 -		memory_deallocate(ptr);
 -	}
 -}
 -
 -static void replace_memory_management()
 -{
 -	// create some document to touch original functions
 -	{
 -		pugi::xml_document doc;
 -		doc.append_child().set_name(STR("node"));
 -	}
 -
 -	// replace functions
 -	pugi::set_memory_management_functions(custom_allocate, custom_deallocate);
 -}
 -
 -#if defined(_MSC_VER) && _MSC_VER > 1200 && _MSC_VER < 1400 && !defined(__INTEL_COMPILER) && !defined(__DMC__)
 -namespace std
 -{
 -	_CRTIMP2 _Prhand _Raise_handler;
 -	_CRTIMP2 void __cdecl _Throw(const exception&) {}
 -}
 -#endif
 -
 -static bool run_test(test_runner* test)
 -{
 -#ifndef PUGIXML_NO_EXCEPTIONS
 -	try
 -	{
 -#endif
 -		g_memory_total_size = 0;
 -		g_memory_total_count = 0;
 -		test_runner::_memory_fail_threshold = 0;
 -	
 -#ifdef _MSC_VER
 -#	pragma warning(push)
 -#	pragma warning(disable: 4611) // interaction between _setjmp and C++ object destruction is non-portable
 -#endif
 -
 -		volatile int result = setjmp(test_runner::_failure_buffer);
 -	
 -#ifdef _MSC_VER
 -#	pragma warning(pop)
 -#endif
 -
 -		if (result)
 -		{
 -			printf("Test %s failed: %s\n", test->_name, test_runner::_failure_message);
 -			return false;
 -		}
 -
 -		test->run();
 -
 -		if (g_memory_total_size != 0 || g_memory_total_count != 0)
 -		{
 -			printf("Test %s failed: memory leaks found (%u bytes in %u allocations)\n", test->_name, (unsigned int)g_memory_total_size, (unsigned int)g_memory_total_count);
 -			return false;
 -		}
 -
 -		return true;
 -#ifndef PUGIXML_NO_EXCEPTIONS
 -	}
 -	catch (const std::exception& e)
 -	{
 -		printf("Test %s failed: exception %s\n", test->_name, e.what());
 -		return false;
 -	}
 -	catch (...)
 -	{
 -		printf("Test %s failed for unknown reason\n", test->_name);
 -		return false;
 -	}
 -#endif
 -}
 -
 -#if defined(__CELLOS_LV2__) && defined(PUGIXML_NO_EXCEPTIONS) && !defined(__SNC__)
 -#include <stdlib.h>
 -
 -void std::exception::_Raise() const
 -{
 -	abort();
 -}
 -#endif
 -
 -int main()
 -{
 -#ifdef __BORLANDC__
 -	_control87(MCW_EM | PC_53, MCW_EM | MCW_PC);
 -#endif
 -
 -	replace_memory_management();
 -
 -	unsigned int total = 0;
 -	unsigned int passed = 0;
 -
 -	test_runner* test = 0; // gcc3 "variable might be used uninitialized in this function" bug workaround
 -
 -	for (test = test_runner::_tests; test; test = test->_next)
 -	{
 -		total++;
 -		passed += run_test(test);
 -	}
 -
 -	unsigned int failed = total - passed;
 -
 -	if (failed != 0)
 -		printf("FAILURE: %u out of %u tests failed.\n", failed, total);
 -	else
 -		printf("Success: %u tests passed.\n", total);
 -
 -	return failed;
 -}
 +#include "test.hpp" +#include "allocator.hpp" + +#include <exception> +#include <stdio.h> +#include <float.h> + +test_runner* test_runner::_tests = 0; +size_t test_runner::_memory_fail_threshold = 0; +jmp_buf test_runner::_failure_buffer; +const char* test_runner::_failure_message; + +static size_t g_memory_total_size = 0; +static size_t g_memory_total_count = 0; + +static void* custom_allocate(size_t size) +{ +	if (test_runner::_memory_fail_threshold > 0 && test_runner::_memory_fail_threshold < g_memory_total_size + size) +		return 0; +	else +	{ +		void* ptr = memory_allocate(size); + +		g_memory_total_size += memory_size(ptr); +		g_memory_total_count++; +		 +		return ptr; +	} +} + +static void custom_deallocate(void* ptr) +{ +	if (ptr) +	{ +		g_memory_total_size -= memory_size(ptr); +		g_memory_total_count--; +		 +		memory_deallocate(ptr); +	} +} + +static void replace_memory_management() +{ +	// create some document to touch original functions +	{ +		pugi::xml_document doc; +		doc.append_child().set_name(STR("node")); +	} + +	// replace functions +	pugi::set_memory_management_functions(custom_allocate, custom_deallocate); +} + +#if defined(_MSC_VER) && _MSC_VER > 1200 && _MSC_VER < 1400 && !defined(__INTEL_COMPILER) && !defined(__DMC__) +namespace std +{ +	_CRTIMP2 _Prhand _Raise_handler; +	_CRTIMP2 void __cdecl _Throw(const exception&) {} +} +#endif + +static bool run_test(test_runner* test) +{ +#ifndef PUGIXML_NO_EXCEPTIONS +	try +	{ +#endif +		g_memory_total_size = 0; +		g_memory_total_count = 0; +		test_runner::_memory_fail_threshold = 0; +	 +#ifdef _MSC_VER +#	pragma warning(push) +#	pragma warning(disable: 4611) // interaction between _setjmp and C++ object destruction is non-portable +#endif + +		volatile int result = setjmp(test_runner::_failure_buffer); +	 +#ifdef _MSC_VER +#	pragma warning(pop) +#endif + +		if (result) +		{ +			printf("Test %s failed: %s\n", test->_name, test_runner::_failure_message); +			return false; +		} + +		test->run(); + +		if (g_memory_total_size != 0 || g_memory_total_count != 0) +		{ +			printf("Test %s failed: memory leaks found (%u bytes in %u allocations)\n", test->_name, (unsigned int)g_memory_total_size, (unsigned int)g_memory_total_count); +			return false; +		} + +		return true; +#ifndef PUGIXML_NO_EXCEPTIONS +	} +	catch (const std::exception& e) +	{ +		printf("Test %s failed: exception %s\n", test->_name, e.what()); +		return false; +	} +	catch (...) +	{ +		printf("Test %s failed for unknown reason\n", test->_name); +		return false; +	} +#endif +} + +#if defined(__CELLOS_LV2__) && defined(PUGIXML_NO_EXCEPTIONS) && !defined(__SNC__) +#include <stdlib.h> + +void std::exception::_Raise() const +{ +	abort(); +} +#endif + +int main() +{ +#ifdef __BORLANDC__ +	_control87(MCW_EM | PC_53, MCW_EM | MCW_PC); +#endif + +	replace_memory_management(); + +	unsigned int total = 0; +	unsigned int passed = 0; + +	test_runner* test = 0; // gcc3 "variable might be used uninitialized in this function" bug workaround + +	for (test = test_runner::_tests; test; test = test->_next) +	{ +		total++; +		passed += run_test(test); +	} + +	unsigned int failed = total - passed; + +	if (failed != 0) +		printf("FAILURE: %u out of %u tests failed.\n", failed, total); +	else +		printf("Success: %u tests passed.\n", total); + +	return failed; +} diff --git a/tests/test.cpp b/tests/test.cpp index 862a0ea..29d74c1 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -1,181 +1,181 @@ -#define _SCL_SECURE_NO_WARNINGS
 -
 -#include "test.hpp"
 -
 -#include "writer_string.hpp"
 -
 -#include <math.h>
 -#include <float.h>
 -
 -#include <algorithm>
 -#include <vector>
 -
 -#ifndef PUGIXML_NO_XPATH
 -static void build_document_order(std::vector<pugi::xpath_node>& result, pugi::xml_node root)
 -{
 -	result.push_back(pugi::xpath_node());
 -
 -	pugi::xml_node cur = root;
 -
 -	for (;;)
 -	{
 -		result.push_back(cur);
 -
 -		for (pugi::xml_attribute a = cur.first_attribute(); a; a = a.next_attribute())
 -			result.push_back(pugi::xpath_node(a, cur));
 -
 -		if (cur.first_child())
 -			cur = cur.first_child();
 -		else if (cur.next_sibling())
 -			cur = cur.next_sibling();
 -		else
 -		{
 -			while (cur && !cur.next_sibling()) cur = cur.parent();
 -			cur = cur.next_sibling();
 -
 -			if (!cur) break;
 -		}
 -	}
 -}
 -#endif
 -
 -bool test_string_equal(const pugi::char_t* lhs, const pugi::char_t* rhs)
 -{
 -	return (!lhs || !rhs) ? lhs == rhs : pugi::impl::strequal(lhs, rhs);
 -}
 -
 -bool test_node(const pugi::xml_node& node, const pugi::char_t* contents, const pugi::char_t* indent, unsigned int flags)
 -{
 -	xml_writer_string writer;
 -
 -	node.print(writer, indent, flags, get_native_encoding());
 -
 -	return writer.as_string() == contents;
 -}
 -
 -#ifndef PUGIXML_NO_XPATH
 -bool test_xpath_string(const pugi::xml_node& node, const pugi::char_t* query, const pugi::char_t* expected)
 -{
 -	pugi::xpath_query q(query);
 -
 -	return q.evaluate_string(node) == expected;
 -}
 -
 -bool test_xpath_boolean(const pugi::xml_node& node, const pugi::char_t* query, bool expected)
 -{
 -	pugi::xpath_query q(query);
 -
 -	return q.evaluate_boolean(node) == expected;
 -}
 -
 -#include <stdio.h>
 -
 -bool test_xpath_number(const pugi::xml_node& node, const pugi::char_t* query, double expected)
 -{
 -	pugi::xpath_query q(query);
 -
 -	double value = q.evaluate_number(node);
 -	double absolute_error = fabs(value - expected);
 -
 -	const double tolerance = 1e-15f;
 -	return absolute_error < tolerance || absolute_error < fabs(expected) * tolerance;
 -}
 -
 -bool test_xpath_number_nan(const pugi::xml_node& node, const pugi::char_t* query)
 -{
 -	pugi::xpath_query q(query);
 -
 -	double r = q.evaluate_number(node);
 -
 -#if defined(_MSC_VER) || defined(__BORLANDC__)
 -	return _isnan(r) != 0;
 -#else
 -	return r != r;
 -#endif
 -}
 -
 -bool test_xpath_fail_compile(const pugi::char_t* query)
 -{
 -	try
 -	{
 -		pugi::xpath_query q(query);
 -		return false;
 -	}
 -	catch (const pugi::xpath_exception&)
 -	{
 -		return true;
 -	}
 -}
 -
 -void xpath_node_set_tester::check(bool condition)
 -{
 -	if (!condition)
 -	{
 -		test_runner::_failure_message = message;
 -		longjmp(test_runner::_failure_buffer, 1);
 -	}
 -}
 -
 -xpath_node_set_tester::xpath_node_set_tester(const pugi::xpath_node_set& set, const char* message): last(0), message(message)
 -{
 -	result = set;
 -
 -	// only sort unsorted sets so that we're able to verify reverse order for some axes
 -	if (result.type() == pugi::xpath_node_set::type_unsorted) result.sort();
 -
 -	if (result.empty())
 -	{
 -		document_order = 0;
 -		document_size = 0;
 -	}
 -	else
 -	{
 -		std::vector<pugi::xpath_node> order;
 -		build_document_order(order, (result[0].attribute() ? result[0].parent() : result[0].node()).root());
 -
 -		document_order = new pugi::xpath_node[order.size()];
 -		std::copy(order.begin(), order.end(), document_order);
 -
 -		document_size = order.size();
 -	}
 -}
 -
 -xpath_node_set_tester::~xpath_node_set_tester()
 -{
 -	// check that we processed everything
 -	check(last == result.size());
 -
 -	delete[] document_order;
 -}
 -
 -xpath_node_set_tester& xpath_node_set_tester::operator%(unsigned int expected)
 -{
 -	// check element count
 -	check(last < result.size());
 -
 -	// check document order
 -	check(expected < document_size);
 -	check(result.begin()[last] == document_order[expected]);
 -
 -	// continue to the next element
 -	last++;
 -
 -	return *this;
 -}
 -
 -#endif
 -
 -bool is_little_endian()
 -{
 -	unsigned int ui = 1;
 -	return *reinterpret_cast<char*>(&ui) == 1;
 -}
 -
 -pugi::xml_encoding get_native_encoding()
 -{
 -#ifdef PUGIXML_WCHAR_MODE
 -	return pugi::encoding_wchar;
 -#else
 -	return pugi::encoding_utf8;
 -#endif
 -}
 +#define _SCL_SECURE_NO_WARNINGS + +#include "test.hpp" + +#include "writer_string.hpp" + +#include <math.h> +#include <float.h> + +#include <algorithm> +#include <vector> + +#ifndef PUGIXML_NO_XPATH +static void build_document_order(std::vector<pugi::xpath_node>& result, pugi::xml_node root) +{ +	result.push_back(pugi::xpath_node()); + +	pugi::xml_node cur = root; + +	for (;;) +	{ +		result.push_back(cur); + +		for (pugi::xml_attribute a = cur.first_attribute(); a; a = a.next_attribute()) +			result.push_back(pugi::xpath_node(a, cur)); + +		if (cur.first_child()) +			cur = cur.first_child(); +		else if (cur.next_sibling()) +			cur = cur.next_sibling(); +		else +		{ +			while (cur && !cur.next_sibling()) cur = cur.parent(); +			cur = cur.next_sibling(); + +			if (!cur) break; +		} +	} +} +#endif + +bool test_string_equal(const pugi::char_t* lhs, const pugi::char_t* rhs) +{ +	return (!lhs || !rhs) ? lhs == rhs : pugi::impl::strequal(lhs, rhs); +} + +bool test_node(const pugi::xml_node& node, const pugi::char_t* contents, const pugi::char_t* indent, unsigned int flags) +{ +	xml_writer_string writer; + +	node.print(writer, indent, flags, get_native_encoding()); + +	return writer.as_string() == contents; +} + +#ifndef PUGIXML_NO_XPATH +bool test_xpath_string(const pugi::xml_node& node, const pugi::char_t* query, const pugi::char_t* expected) +{ +	pugi::xpath_query q(query); + +	return q.evaluate_string(node) == expected; +} + +bool test_xpath_boolean(const pugi::xml_node& node, const pugi::char_t* query, bool expected) +{ +	pugi::xpath_query q(query); + +	return q.evaluate_boolean(node) == expected; +} + +#include <stdio.h> + +bool test_xpath_number(const pugi::xml_node& node, const pugi::char_t* query, double expected) +{ +	pugi::xpath_query q(query); + +	double value = q.evaluate_number(node); +	double absolute_error = fabs(value - expected); + +	const double tolerance = 1e-15f; +	return absolute_error < tolerance || absolute_error < fabs(expected) * tolerance; +} + +bool test_xpath_number_nan(const pugi::xml_node& node, const pugi::char_t* query) +{ +	pugi::xpath_query q(query); + +	double r = q.evaluate_number(node); + +#if defined(_MSC_VER) || defined(__BORLANDC__) +	return _isnan(r) != 0; +#else +	return r != r; +#endif +} + +bool test_xpath_fail_compile(const pugi::char_t* query) +{ +	try +	{ +		pugi::xpath_query q(query); +		return false; +	} +	catch (const pugi::xpath_exception&) +	{ +		return true; +	} +} + +void xpath_node_set_tester::check(bool condition) +{ +	if (!condition) +	{ +		test_runner::_failure_message = message; +		longjmp(test_runner::_failure_buffer, 1); +	} +} + +xpath_node_set_tester::xpath_node_set_tester(const pugi::xpath_node_set& set, const char* message): last(0), message(message) +{ +	result = set; + +	// only sort unsorted sets so that we're able to verify reverse order for some axes +	if (result.type() == pugi::xpath_node_set::type_unsorted) result.sort(); + +	if (result.empty()) +	{ +		document_order = 0; +		document_size = 0; +	} +	else +	{ +		std::vector<pugi::xpath_node> order; +		build_document_order(order, (result[0].attribute() ? result[0].parent() : result[0].node()).root()); + +		document_order = new pugi::xpath_node[order.size()]; +		std::copy(order.begin(), order.end(), document_order); + +		document_size = order.size(); +	} +} + +xpath_node_set_tester::~xpath_node_set_tester() +{ +	// check that we processed everything +	check(last == result.size()); + +	delete[] document_order; +} + +xpath_node_set_tester& xpath_node_set_tester::operator%(unsigned int expected) +{ +	// check element count +	check(last < result.size()); + +	// check document order +	check(expected < document_size); +	check(result.begin()[last] == document_order[expected]); + +	// continue to the next element +	last++; + +	return *this; +} + +#endif + +bool is_little_endian() +{ +	unsigned int ui = 1; +	return *reinterpret_cast<char*>(&ui) == 1; +} + +pugi::xml_encoding get_native_encoding() +{ +#ifdef PUGIXML_WCHAR_MODE +	return pugi::encoding_wchar; +#else +	return pugi::encoding_utf8; +#endif +} diff --git a/tests/test.hpp b/tests/test.hpp index d4b5879..c269fb5 100644 --- a/tests/test.hpp +++ b/tests/test.hpp @@ -1,151 +1,151 @@ -#ifndef HEADER_TEST_TEST_HPP
 -#define HEADER_TEST_TEST_HPP
 -
 -#include "../src/pugixml.hpp"
 -
 -#include <setjmp.h>
 -
 -struct test_runner
 -{
 -	test_runner(const char* name)
 -	{
 -		_name = name;
 -		_next = _tests;
 -		_tests = this;
 -	}
 -
 -	virtual ~test_runner() {}
 -
 -	virtual void run() = 0;
 -
 -	const char* _name;
 -	test_runner* _next;
 -
 -	static test_runner* _tests;
 -	static size_t _memory_fail_threshold;
 -	static jmp_buf _failure_buffer;
 -	static const char* _failure_message;
 -};
 -
 -bool test_string_equal(const pugi::char_t* lhs, const pugi::char_t* rhs);
 -
 -template <typename Node> inline bool test_node_name_value(const Node& node, const pugi::char_t* name, const pugi::char_t* value)
 -{
 -	return test_string_equal(node.name(), name) && test_string_equal(node.value(), value);
 -}
 -
 -bool test_node(const pugi::xml_node& node, const pugi::char_t* contents, const pugi::char_t* indent, unsigned int flags);
 -
 -#ifndef PUGIXML_NO_XPATH
 -bool test_xpath_string(const pugi::xml_node& node, const pugi::char_t* query, const pugi::char_t* expected);
 -bool test_xpath_boolean(const pugi::xml_node& node, const pugi::char_t* query, bool expected);
 -bool test_xpath_number(const pugi::xml_node& node, const pugi::char_t* query, double expected);
 -bool test_xpath_number_nan(const pugi::xml_node& node, const pugi::char_t* query);
 -bool test_xpath_fail_compile(const pugi::char_t* query);
 -
 -struct xpath_node_set_tester
 -{
 -	pugi::xpath_node* document_order;
 -	size_t document_size;
 -
 -	pugi::xpath_node_set result;
 -	unsigned int last;
 -	const char* message;
 -
 -	void check(bool condition);
 -
 -	xpath_node_set_tester(const pugi::xpath_node_set& set, const char* message);
 -	~xpath_node_set_tester();
 -
 -	xpath_node_set_tester& operator%(unsigned int expected);
 -};
 -
 -#endif
 -
 -struct dummy_fixture {};
 -
 -#define TEST_FIXTURE(name, fixture) \
 -	struct test_runner_helper_##name: fixture \
 -	{ \
 -		void run(); \
 -	}; \
 -	static struct test_runner_##name: test_runner \
 -	{ \
 -		test_runner_##name(): test_runner(#name) {} \
 -		\
 -		virtual void run() \
 -		{ \
 -			test_runner_helper_##name helper; \
 -			helper.run(); \
 -		} \
 -	} test_runner_instance_##name; \
 -	void test_runner_helper_##name::run()
 -
 -#define TEST(name) TEST_FIXTURE(name, dummy_fixture)
 -
 -#define TEST_XML_FLAGS(name, xml, flags) \
 -	struct test_fixture_##name \
 -	{ \
 -		pugi::xml_document doc; \
 -		\
 -		test_fixture_##name() \
 -		{ \
 -			CHECK(doc.load(PUGIXML_TEXT(xml), flags)); \
 -		} \
 -		\
 -	private: \
 -		test_fixture_##name(const test_fixture_##name&); \
 -		test_fixture_##name& operator=(const test_fixture_##name&); \
 -	}; \
 -	\
 -	TEST_FIXTURE(name, test_fixture_##name)
 -
 -#define TEST_XML(name, xml) TEST_XML_FLAGS(name, xml, pugi::parse_default)
 -
 -#define CHECK_JOIN(text, file, line) text file #line
 -#define CHECK_JOIN2(text, file, line) CHECK_JOIN(text, file, line)
 -#define CHECK_TEXT(condition, text) if (condition) ; else test_runner::_failure_message = CHECK_JOIN2(text, " at "__FILE__ ":", __LINE__), longjmp(test_runner::_failure_buffer, 1)
 -
 -#if (defined(_MSC_VER) && _MSC_VER == 1200) || defined(__MWERKS__)
 -#	define STRINGIZE(value) "??" // MSVC 6.0 and CodeWarrior have troubles stringizing stuff with strings w/escaping inside
 -#else
 -#	define STRINGIZE(value) #value
 -#endif
 -
 -#define CHECK(condition) CHECK_TEXT(condition, STRINGIZE(condition) " is false")
 -#define CHECK_STRING(value, expected) CHECK_TEXT(test_string_equal(value, expected), STRINGIZE(value) " is not equal to " STRINGIZE(expected))
 -#define CHECK_DOUBLE(value, expected) CHECK_TEXT((value > expected ? value - expected : expected - value) < 1e-6, STRINGIZE(value) " is not equal to " STRINGIZE(expected))
 -#define CHECK_NAME_VALUE(node, name, value) CHECK_TEXT(test_node_name_value(node, name, value), STRINGIZE(node) " name/value do not match " STRINGIZE(name) " and " STRINGIZE(value))
 -#define CHECK_NODE_EX(node, expected, indent, flags) CHECK_TEXT(test_node(node, expected, indent, flags), STRINGIZE(node) " contents does not match " STRINGIZE(expected))
 -#define CHECK_NODE(node, expected) CHECK_NODE_EX(node, expected, PUGIXML_TEXT(""), pugi::format_raw)
 -
 -#ifndef PUGIXML_NO_XPATH
 -#define CHECK_XPATH_STRING(node, query, expected) CHECK_TEXT(test_xpath_string(node, query, expected), STRINGIZE(query) " does not evaluate to " STRINGIZE(expected) " in context " STRINGIZE(node))
 -#define CHECK_XPATH_BOOLEAN(node, query, expected) CHECK_TEXT(test_xpath_boolean(node, query, expected), STRINGIZE(query) " does not evaluate to " STRINGIZE(expected) " in context " STRINGIZE(node))
 -#define CHECK_XPATH_NUMBER(node, query, expected) CHECK_TEXT(test_xpath_number(node, query, expected), STRINGIZE(query) " does not evaluate to " STRINGIZE(expected) " in context " STRINGIZE(node))
 -#define CHECK_XPATH_NUMBER_NAN(node, query) CHECK_TEXT(test_xpath_number_nan(node, query), STRINGIZE(query) " does not evaluate to NaN in context " STRINGIZE(node))
 -#define CHECK_XPATH_FAIL(query) CHECK_TEXT(test_xpath_fail_compile(query), STRINGIZE(query) " should not compile")
 -#define CHECK_XPATH_NODESET(node, query) xpath_node_set_tester(node.select_nodes(query), CHECK_JOIN2(STRINGIZE(query) " does not evaluate to expected set in context " STRINGIZE(node), " at "__FILE__ ":", __LINE__))
 -#endif
 -
 -#define STR(text) PUGIXML_TEXT(text)
 -
 -#ifdef __DMC__
 -#define U_LITERALS // DMC does not understand \x01234 (it parses first three digits), but understands \u01234
 -#endif
 -
 -#if (defined(_MSC_VER) && _MSC_VER == 1200) || (defined(__INTEL_COMPILER) && __INTEL_COMPILER == 800) || defined(__BORLANDC__)
 -// NaN comparison on MSVC6 is incorrect, see http://www.nabble.com/assertDoubleEquals,-NaN---Microsoft-Visual-Studio-6-td9137859.html
 -// IC8 and BCC are also affected by the same bug
 -#	define MSVC6_NAN_BUG 
 -#endif
 -
 -inline wchar_t wchar_cast(unsigned int value)
 -{
 -	return static_cast<wchar_t>(value); // to avoid C4310 on MSVC
 -}
 -
 -bool is_little_endian();
 -pugi::xml_encoding get_native_encoding();
 -
 -#endif
 +#ifndef HEADER_TEST_TEST_HPP +#define HEADER_TEST_TEST_HPP + +#include "../src/pugixml.hpp" + +#include <setjmp.h> + +struct test_runner +{ +	test_runner(const char* name) +	{ +		_name = name; +		_next = _tests; +		_tests = this; +	} + +	virtual ~test_runner() {} + +	virtual void run() = 0; + +	const char* _name; +	test_runner* _next; + +	static test_runner* _tests; +	static size_t _memory_fail_threshold; +	static jmp_buf _failure_buffer; +	static const char* _failure_message; +}; + +bool test_string_equal(const pugi::char_t* lhs, const pugi::char_t* rhs); + +template <typename Node> inline bool test_node_name_value(const Node& node, const pugi::char_t* name, const pugi::char_t* value) +{ +	return test_string_equal(node.name(), name) && test_string_equal(node.value(), value); +} + +bool test_node(const pugi::xml_node& node, const pugi::char_t* contents, const pugi::char_t* indent, unsigned int flags); + +#ifndef PUGIXML_NO_XPATH +bool test_xpath_string(const pugi::xml_node& node, const pugi::char_t* query, const pugi::char_t* expected); +bool test_xpath_boolean(const pugi::xml_node& node, const pugi::char_t* query, bool expected); +bool test_xpath_number(const pugi::xml_node& node, const pugi::char_t* query, double expected); +bool test_xpath_number_nan(const pugi::xml_node& node, const pugi::char_t* query); +bool test_xpath_fail_compile(const pugi::char_t* query); + +struct xpath_node_set_tester +{ +	pugi::xpath_node* document_order; +	size_t document_size; + +	pugi::xpath_node_set result; +	unsigned int last; +	const char* message; + +	void check(bool condition); + +	xpath_node_set_tester(const pugi::xpath_node_set& set, const char* message); +	~xpath_node_set_tester(); + +	xpath_node_set_tester& operator%(unsigned int expected); +}; + +#endif + +struct dummy_fixture {}; + +#define TEST_FIXTURE(name, fixture) \ +	struct test_runner_helper_##name: fixture \ +	{ \ +		void run(); \ +	}; \ +	static struct test_runner_##name: test_runner \ +	{ \ +		test_runner_##name(): test_runner(#name) {} \ +		\ +		virtual void run() \ +		{ \ +			test_runner_helper_##name helper; \ +			helper.run(); \ +		} \ +	} test_runner_instance_##name; \ +	void test_runner_helper_##name::run() + +#define TEST(name) TEST_FIXTURE(name, dummy_fixture) + +#define TEST_XML_FLAGS(name, xml, flags) \ +	struct test_fixture_##name \ +	{ \ +		pugi::xml_document doc; \ +		\ +		test_fixture_##name() \ +		{ \ +			CHECK(doc.load(PUGIXML_TEXT(xml), flags)); \ +		} \ +		\ +	private: \ +		test_fixture_##name(const test_fixture_##name&); \ +		test_fixture_##name& operator=(const test_fixture_##name&); \ +	}; \ +	\ +	TEST_FIXTURE(name, test_fixture_##name) + +#define TEST_XML(name, xml) TEST_XML_FLAGS(name, xml, pugi::parse_default) + +#define CHECK_JOIN(text, file, line) text file #line +#define CHECK_JOIN2(text, file, line) CHECK_JOIN(text, file, line) +#define CHECK_TEXT(condition, text) if (condition) ; else test_runner::_failure_message = CHECK_JOIN2(text, " at "__FILE__ ":", __LINE__), longjmp(test_runner::_failure_buffer, 1) + +#if (defined(_MSC_VER) && _MSC_VER == 1200) || defined(__MWERKS__) +#	define STRINGIZE(value) "??" // MSVC 6.0 and CodeWarrior have troubles stringizing stuff with strings w/escaping inside +#else +#	define STRINGIZE(value) #value +#endif + +#define CHECK(condition) CHECK_TEXT(condition, STRINGIZE(condition) " is false") +#define CHECK_STRING(value, expected) CHECK_TEXT(test_string_equal(value, expected), STRINGIZE(value) " is not equal to " STRINGIZE(expected)) +#define CHECK_DOUBLE(value, expected) CHECK_TEXT((value > expected ? value - expected : expected - value) < 1e-6, STRINGIZE(value) " is not equal to " STRINGIZE(expected)) +#define CHECK_NAME_VALUE(node, name, value) CHECK_TEXT(test_node_name_value(node, name, value), STRINGIZE(node) " name/value do not match " STRINGIZE(name) " and " STRINGIZE(value)) +#define CHECK_NODE_EX(node, expected, indent, flags) CHECK_TEXT(test_node(node, expected, indent, flags), STRINGIZE(node) " contents does not match " STRINGIZE(expected)) +#define CHECK_NODE(node, expected) CHECK_NODE_EX(node, expected, PUGIXML_TEXT(""), pugi::format_raw) + +#ifndef PUGIXML_NO_XPATH +#define CHECK_XPATH_STRING(node, query, expected) CHECK_TEXT(test_xpath_string(node, query, expected), STRINGIZE(query) " does not evaluate to " STRINGIZE(expected) " in context " STRINGIZE(node)) +#define CHECK_XPATH_BOOLEAN(node, query, expected) CHECK_TEXT(test_xpath_boolean(node, query, expected), STRINGIZE(query) " does not evaluate to " STRINGIZE(expected) " in context " STRINGIZE(node)) +#define CHECK_XPATH_NUMBER(node, query, expected) CHECK_TEXT(test_xpath_number(node, query, expected), STRINGIZE(query) " does not evaluate to " STRINGIZE(expected) " in context " STRINGIZE(node)) +#define CHECK_XPATH_NUMBER_NAN(node, query) CHECK_TEXT(test_xpath_number_nan(node, query), STRINGIZE(query) " does not evaluate to NaN in context " STRINGIZE(node)) +#define CHECK_XPATH_FAIL(query) CHECK_TEXT(test_xpath_fail_compile(query), STRINGIZE(query) " should not compile") +#define CHECK_XPATH_NODESET(node, query) xpath_node_set_tester(node.select_nodes(query), CHECK_JOIN2(STRINGIZE(query) " does not evaluate to expected set in context " STRINGIZE(node), " at "__FILE__ ":", __LINE__)) +#endif + +#define STR(text) PUGIXML_TEXT(text) + +#ifdef __DMC__ +#define U_LITERALS // DMC does not understand \x01234 (it parses first three digits), but understands \u01234 +#endif + +#if (defined(_MSC_VER) && _MSC_VER == 1200) || (defined(__INTEL_COMPILER) && __INTEL_COMPILER == 800) || defined(__BORLANDC__) +// NaN comparison on MSVC6 is incorrect, see http://www.nabble.com/assertDoubleEquals,-NaN---Microsoft-Visual-Studio-6-td9137859.html +// IC8 and BCC are also affected by the same bug +#	define MSVC6_NAN_BUG  +#endif + +inline wchar_t wchar_cast(unsigned int value) +{ +	return static_cast<wchar_t>(value); // to avoid C4310 on MSVC +} + +bool is_little_endian(); +pugi::xml_encoding get_native_encoding(); + +#endif diff --git a/tests/test_deprecated.cpp b/tests/test_deprecated.cpp index d81810e..4d97b2a 100644 --- a/tests/test_deprecated.cpp +++ b/tests/test_deprecated.cpp @@ -1,203 +1,203 @@ -// This file includes all tests for deprecated functionality; this is going away in the next release!
 -
 -#ifdef _MSC_VER
 -#	pragma warning(disable: 4996)
 -#endif
 -
 -#ifdef __GNUC__
 -#	if __GNUC__ >= 4 && __GNUC_MINOR__ >= 2
 -#		pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 -#	else
 -#		define PUGIXML_DEPRECATED
 -#	endif
 -#endif
 -
 -#ifdef __INTEL_COMPILER
 -#	pragma warning(disable: 1478)
 -#endif
 -
 -#include <string.h>
 -
 -#include "common.hpp"
 -
 -#include "writer_string.hpp"
 -
 -#include <vector>
 -#include <iterator>
 -
 -// format_write_bom_utf8 - it's now format_write_bom!
 -TEST_XML(document_save_bom_utf8, "<node/>")
 -{
 -	xml_writer_string writer;
 -
 -	CHECK(test_save_narrow(doc, pugi::format_no_declaration | pugi::format_raw | pugi::format_write_bom_utf8, encoding_utf8, "\xef\xbb\xbf<node />", 11));
 -}
 -
 -// parse - it's now load_buffer_inplace
 -TEST(document_parse)
 -{
 -	char text[] = "<node/>";
 -
 -	pugi::xml_document doc;
 -
 -	CHECK(doc.parse(text));
 -	CHECK_NODE(doc, STR("<node />"));
 -}
 -
 -// parse with transfer_ownership_tag attribute - it's now load_buffer_inplace_own
 -TEST(document_parse_transfer_ownership)
 -{
 -	allocation_function alloc = get_memory_allocation_function();
 -
 -	char* text = static_cast<char*>(alloc(strlen("<node/>") + 1));
 -	CHECK(text);
 -
 -	memcpy(text, "<node/>", strlen("<node/>") + 1);
 -
 -	pugi::xml_document doc;
 -
 -	CHECK(doc.parse(transfer_ownership_tag(), text));
 -	CHECK_NODE(doc, STR("<node />"));
 -}
 -
 -#ifndef PUGIXML_NO_STL
 -// as_utf16 - it's now as_wide
 -TEST(as_utf16)
 -{
 -	CHECK(as_utf16("") == L"");
 -
 -	// valid 1-byte, 2-byte and 3-byte inputs
 -#ifdef U_LITERALS
 -	CHECK(as_utf16("?\xd0\x80\xe2\x80\xbd") == L"?\u0400\u203D");
 -#else
 -	CHECK(as_utf16("?\xd0\x80\xe2\x80\xbd") == L"?\x0400\x203D");
 -#endif
 -}
 -#endif
 -
 -// wildcard functions
 -TEST_XML(dom_node_child_w, "<node><child1/><child2/></node>")
 -{
 -	CHECK(doc.child_w(STR("n?de")) == doc.child(STR("node")));
 -	CHECK(doc.child_w(STR("n[az]de")) == xml_node());
 -	CHECK(doc.child_w(STR("n[aoz]de")) == doc.child(STR("node")));
 -	CHECK(doc.child_w(STR("*e")) == doc.child(STR("node")));
 -	CHECK(doc.child(STR("node")).child_w(STR("*l?[23456789]*")) == doc.child(STR("node")).child(STR("child2")));
 -}
 -
 -TEST_XML(dom_node_attribute_w, "<node attr1='0' attr2='1'/>")
 -{
 -	xml_node node = doc.child(STR("node"));
 -
 -	CHECK(node.attribute_w(STR("*tt?[23456789]*")) == node.attribute(STR("attr2")));
 -	CHECK(node.attribute_w(STR("?")) == xml_attribute());
 -}
 -
 -TEST_XML(dom_node_next_previous_sibling_w, "<node><child1/><child2/><child3/></node>")
 -{
 -	CHECK(xml_node().next_sibling_w(STR("n")) == xml_node());
 -	CHECK(xml_node().previous_sibling_w(STR("n")) == xml_node());
 -
 -	xml_node child1 = doc.child(STR("node")).child(STR("child1"));
 -	xml_node child3 = doc.child(STR("node")).child(STR("child3"));
 -
 -	CHECK(child1.next_sibling_w(STR("*[3456789]")) == child3);
 -	CHECK(child1.next_sibling_w(STR("?")) == xml_node());
 -	CHECK(child3.previous_sibling_w(STR("*[3456789]")) == xml_node());
 -	CHECK(child3.previous_sibling_w(STR("?")) == xml_node());
 -	CHECK(child3.previous_sibling_w(STR("*1")) == child1);
 -}
 -
 -TEST_XML(dom_node_child_value_w, "<node><novalue/><child1>value1</child1><child2>value2<n/></child2><child3><![CDATA[value3]]></child3>value4</node>")
 -{
 -	CHECK_STRING(xml_node().child_value_w(STR("n")), STR(""));
 -
 -	xml_node node = doc.child(STR("node"));
 -
 -	CHECK_STRING(node.child_value_w(STR("c*[23456789]")), STR("value2"));
 -	CHECK_STRING(node.child_value_w(STR("*")), STR("")); // child_value(name) and child_value_w(pattern) do not continue the search if a node w/out value is found first
 -	CHECK_STRING(node.child_value_w(STR("nothing*here")), STR(""));
 -}
 -
 -TEST_XML(dom_node_find_child_by_attribute_w, "<node><child1 attr='value1'/><child2 attr='value2'/><child2 attr='value3'/></node>")
 -{
 -	CHECK(xml_node().find_child_by_attribute_w(STR("name"), STR("attr"), STR("value")) == xml_node());
 -	CHECK(xml_node().find_child_by_attribute_w(STR("attr"), STR("value")) == xml_node());
 -
 -	xml_node node = doc.child(STR("node"));
 -
 -	CHECK(node.find_child_by_attribute_w(STR("*"), STR("att?"), STR("val*[0123456789]")) == node.child(STR("child1")));
 -	CHECK(node.find_child_by_attribute_w(STR("*"), STR("attr3"), STR("val*[0123456789]")) == xml_node());
 -	CHECK(node.find_child_by_attribute_w(STR("att?"), STR("val*[0123456789]")) == node.child(STR("child1")));
 -	CHECK(node.find_child_by_attribute_w(STR("attr3"), STR("val*[0123456789]")) == xml_node());
 -}
 -
 -TEST_XML(dom_node_all_elements_by_name, "<node><child><child/><child/></child></node>")
 -{
 -	std::vector<xml_node> v;
 -
 -	v.clear();
 -	xml_node().all_elements_by_name(STR("node"), std::back_inserter(v));
 -	CHECK(v.empty());
 -
 -	v.clear();
 -	doc.all_elements_by_name(STR("node"), std::back_inserter(v));
 -	CHECK(v.size() == 1 && v[0] == doc.child(STR("node")));
 -
 -	v.clear();
 -	doc.all_elements_by_name(STR("child"), std::back_inserter(v));
 -	CHECK(v.size() == 3);
 -	CHECK(v[0] == doc.child(STR("node")).child(STR("child")));
 -	CHECK(v[1] == doc.child(STR("node")).child(STR("child")).first_child());
 -	CHECK(v[2] == doc.child(STR("node")).child(STR("child")).last_child());
 -}
 -
 -TEST_XML(dom_node_all_elements_by_name_w, "<node><child><child/><child/></child></node>")
 -{
 -	std::vector<xml_node> v;
 -
 -	v.clear();
 -	xml_node().all_elements_by_name_w(STR("*"), std::back_inserter(v));
 -	CHECK(v.empty());
 -
 -	v.clear();
 -	doc.all_elements_by_name_w(STR("*"), std::back_inserter(v));
 -	CHECK(v.size() == 4);
 -	CHECK(v[0] == doc.child(STR("node")));
 -	CHECK(v[1] == doc.child(STR("node")).child(STR("child")));
 -	CHECK(v[2] == doc.child(STR("node")).child(STR("child")).first_child());
 -	CHECK(v[3] == doc.child(STR("node")).child(STR("child")).last_child());
 -}
 -
 -TEST_XML(dom_node_wildcard_cset, "<node c='1'/>")
 -{
 -	xml_node node = doc.child(STR("node"));
 -
 -	CHECK(node.attribute_w(STR("[A-Z]")).as_int() == 0);
 -	CHECK(node.attribute_w(STR("[a-z]")).as_int() == 1);
 -	CHECK(node.attribute_w(STR("[A-z]")).as_int() == 1);
 -	CHECK(node.attribute_w(STR("[z-a]")).as_int() == 0);
 -	CHECK(node.attribute_w(STR("[a-zA-Z]")).as_int() == 1);
 -	CHECK(node.attribute_w(STR("[!A-Z]")).as_int() == 1);
 -	CHECK(node.attribute_w(STR("[!A-Za-z]")).as_int() == 0);
 -}
 -
 -TEST_XML(dom_node_wildcard_star, "<node cd='1'/>")
 -{
 -	xml_node node = doc.child(STR("node"));
 -
 -	CHECK(node.attribute_w(STR("*")).as_int() == 1);
 -	CHECK(node.attribute_w(STR("?d*")).as_int() == 1);
 -	CHECK(node.attribute_w(STR("?c*")).as_int() == 0);
 -	CHECK(node.attribute_w(STR("*?*c*")).as_int() == 0);
 -	CHECK(node.attribute_w(STR("*?*d*")).as_int() == 1);
 -}
 -
 -// document order
 -TEST_XML(document_order_coverage, "<node id='1'/>")
 -{
 -	doc.precompute_document_order();
 -
 -	CHECK(doc.child(STR("node")).document_order() == 0);
 -	CHECK(doc.child(STR("node")).attribute(STR("id")).document_order() == 0);
 -}
 +// This file includes all tests for deprecated functionality; this is going away in the next release! + +#ifdef _MSC_VER +#	pragma warning(disable: 4996) +#endif + +#ifdef __GNUC__ +#	if __GNUC__ >= 4 && __GNUC_MINOR__ >= 2 +#		pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#	else +#		define PUGIXML_DEPRECATED +#	endif +#endif + +#ifdef __INTEL_COMPILER +#	pragma warning(disable: 1478) +#endif + +#include <string.h> + +#include "common.hpp" + +#include "writer_string.hpp" + +#include <vector> +#include <iterator> + +// format_write_bom_utf8 - it's now format_write_bom! +TEST_XML(document_save_bom_utf8, "<node/>") +{ +	xml_writer_string writer; + +	CHECK(test_save_narrow(doc, pugi::format_no_declaration | pugi::format_raw | pugi::format_write_bom_utf8, encoding_utf8, "\xef\xbb\xbf<node />", 11)); +} + +// parse - it's now load_buffer_inplace +TEST(document_parse) +{ +	char text[] = "<node/>"; + +	pugi::xml_document doc; + +	CHECK(doc.parse(text)); +	CHECK_NODE(doc, STR("<node />")); +} + +// parse with transfer_ownership_tag attribute - it's now load_buffer_inplace_own +TEST(document_parse_transfer_ownership) +{ +	allocation_function alloc = get_memory_allocation_function(); + +	char* text = static_cast<char*>(alloc(strlen("<node/>") + 1)); +	CHECK(text); + +	memcpy(text, "<node/>", strlen("<node/>") + 1); + +	pugi::xml_document doc; + +	CHECK(doc.parse(transfer_ownership_tag(), text)); +	CHECK_NODE(doc, STR("<node />")); +} + +#ifndef PUGIXML_NO_STL +// as_utf16 - it's now as_wide +TEST(as_utf16) +{ +	CHECK(as_utf16("") == L""); + +	// valid 1-byte, 2-byte and 3-byte inputs +#ifdef U_LITERALS +	CHECK(as_utf16("?\xd0\x80\xe2\x80\xbd") == L"?\u0400\u203D"); +#else +	CHECK(as_utf16("?\xd0\x80\xe2\x80\xbd") == L"?\x0400\x203D"); +#endif +} +#endif + +// wildcard functions +TEST_XML(dom_node_child_w, "<node><child1/><child2/></node>") +{ +	CHECK(doc.child_w(STR("n?de")) == doc.child(STR("node"))); +	CHECK(doc.child_w(STR("n[az]de")) == xml_node()); +	CHECK(doc.child_w(STR("n[aoz]de")) == doc.child(STR("node"))); +	CHECK(doc.child_w(STR("*e")) == doc.child(STR("node"))); +	CHECK(doc.child(STR("node")).child_w(STR("*l?[23456789]*")) == doc.child(STR("node")).child(STR("child2"))); +} + +TEST_XML(dom_node_attribute_w, "<node attr1='0' attr2='1'/>") +{ +	xml_node node = doc.child(STR("node")); + +	CHECK(node.attribute_w(STR("*tt?[23456789]*")) == node.attribute(STR("attr2"))); +	CHECK(node.attribute_w(STR("?")) == xml_attribute()); +} + +TEST_XML(dom_node_next_previous_sibling_w, "<node><child1/><child2/><child3/></node>") +{ +	CHECK(xml_node().next_sibling_w(STR("n")) == xml_node()); +	CHECK(xml_node().previous_sibling_w(STR("n")) == xml_node()); + +	xml_node child1 = doc.child(STR("node")).child(STR("child1")); +	xml_node child3 = doc.child(STR("node")).child(STR("child3")); + +	CHECK(child1.next_sibling_w(STR("*[3456789]")) == child3); +	CHECK(child1.next_sibling_w(STR("?")) == xml_node()); +	CHECK(child3.previous_sibling_w(STR("*[3456789]")) == xml_node()); +	CHECK(child3.previous_sibling_w(STR("?")) == xml_node()); +	CHECK(child3.previous_sibling_w(STR("*1")) == child1); +} + +TEST_XML(dom_node_child_value_w, "<node><novalue/><child1>value1</child1><child2>value2<n/></child2><child3><![CDATA[value3]]></child3>value4</node>") +{ +	CHECK_STRING(xml_node().child_value_w(STR("n")), STR("")); + +	xml_node node = doc.child(STR("node")); + +	CHECK_STRING(node.child_value_w(STR("c*[23456789]")), STR("value2")); +	CHECK_STRING(node.child_value_w(STR("*")), STR("")); // child_value(name) and child_value_w(pattern) do not continue the search if a node w/out value is found first +	CHECK_STRING(node.child_value_w(STR("nothing*here")), STR("")); +} + +TEST_XML(dom_node_find_child_by_attribute_w, "<node><child1 attr='value1'/><child2 attr='value2'/><child2 attr='value3'/></node>") +{ +	CHECK(xml_node().find_child_by_attribute_w(STR("name"), STR("attr"), STR("value")) == xml_node()); +	CHECK(xml_node().find_child_by_attribute_w(STR("attr"), STR("value")) == xml_node()); + +	xml_node node = doc.child(STR("node")); + +	CHECK(node.find_child_by_attribute_w(STR("*"), STR("att?"), STR("val*[0123456789]")) == node.child(STR("child1"))); +	CHECK(node.find_child_by_attribute_w(STR("*"), STR("attr3"), STR("val*[0123456789]")) == xml_node()); +	CHECK(node.find_child_by_attribute_w(STR("att?"), STR("val*[0123456789]")) == node.child(STR("child1"))); +	CHECK(node.find_child_by_attribute_w(STR("attr3"), STR("val*[0123456789]")) == xml_node()); +} + +TEST_XML(dom_node_all_elements_by_name, "<node><child><child/><child/></child></node>") +{ +	std::vector<xml_node> v; + +	v.clear(); +	xml_node().all_elements_by_name(STR("node"), std::back_inserter(v)); +	CHECK(v.empty()); + +	v.clear(); +	doc.all_elements_by_name(STR("node"), std::back_inserter(v)); +	CHECK(v.size() == 1 && v[0] == doc.child(STR("node"))); + +	v.clear(); +	doc.all_elements_by_name(STR("child"), std::back_inserter(v)); +	CHECK(v.size() == 3); +	CHECK(v[0] == doc.child(STR("node")).child(STR("child"))); +	CHECK(v[1] == doc.child(STR("node")).child(STR("child")).first_child()); +	CHECK(v[2] == doc.child(STR("node")).child(STR("child")).last_child()); +} + +TEST_XML(dom_node_all_elements_by_name_w, "<node><child><child/><child/></child></node>") +{ +	std::vector<xml_node> v; + +	v.clear(); +	xml_node().all_elements_by_name_w(STR("*"), std::back_inserter(v)); +	CHECK(v.empty()); + +	v.clear(); +	doc.all_elements_by_name_w(STR("*"), std::back_inserter(v)); +	CHECK(v.size() == 4); +	CHECK(v[0] == doc.child(STR("node"))); +	CHECK(v[1] == doc.child(STR("node")).child(STR("child"))); +	CHECK(v[2] == doc.child(STR("node")).child(STR("child")).first_child()); +	CHECK(v[3] == doc.child(STR("node")).child(STR("child")).last_child()); +} + +TEST_XML(dom_node_wildcard_cset, "<node c='1'/>") +{ +	xml_node node = doc.child(STR("node")); + +	CHECK(node.attribute_w(STR("[A-Z]")).as_int() == 0); +	CHECK(node.attribute_w(STR("[a-z]")).as_int() == 1); +	CHECK(node.attribute_w(STR("[A-z]")).as_int() == 1); +	CHECK(node.attribute_w(STR("[z-a]")).as_int() == 0); +	CHECK(node.attribute_w(STR("[a-zA-Z]")).as_int() == 1); +	CHECK(node.attribute_w(STR("[!A-Z]")).as_int() == 1); +	CHECK(node.attribute_w(STR("[!A-Za-z]")).as_int() == 0); +} + +TEST_XML(dom_node_wildcard_star, "<node cd='1'/>") +{ +	xml_node node = doc.child(STR("node")); + +	CHECK(node.attribute_w(STR("*")).as_int() == 1); +	CHECK(node.attribute_w(STR("?d*")).as_int() == 1); +	CHECK(node.attribute_w(STR("?c*")).as_int() == 0); +	CHECK(node.attribute_w(STR("*?*c*")).as_int() == 0); +	CHECK(node.attribute_w(STR("*?*d*")).as_int() == 1); +} + +// document order +TEST_XML(document_order_coverage, "<node id='1'/>") +{ +	doc.precompute_document_order(); + +	CHECK(doc.child(STR("node")).document_order() == 0); +	CHECK(doc.child(STR("node")).attribute(STR("id")).document_order() == 0); +} diff --git a/tests/test_document.cpp b/tests/test_document.cpp index 9a83a6d..1f781e2 100644 --- a/tests/test_document.cpp +++ b/tests/test_document.cpp @@ -1,710 +1,710 @@ -#define _CRT_SECURE_NO_WARNINGS
 -#define _CRT_NONSTDC_NO_DEPRECATE 0
 -
 -#include <string.h> // because Borland's STL is braindead, we have to include <string.h> _before_ <string> in order to get memcpy
 -
 -#include "common.hpp"
 -
 -#include "writer_string.hpp"
 -
 -#include <stdio.h>
 -#include <stdlib.h>
 -
 -#include <fstream>
 -#include <sstream>
 -
 -#include <string>
 -
 -#ifdef __MINGW32__
 -#	include <io.h> // for unlink in C++0x mode
 -#endif
 -
 -#if defined(__CELLOS_LV2__)
 -#	include <unistd.h> // for unlink
 -#endif
 -
 -TEST(document_create_empty)
 -{
 -	pugi::xml_document doc;
 -	CHECK_NODE(doc, STR(""));
 -}
 -
 -TEST(document_create)
 -{
 -	pugi::xml_document doc;
 -	doc.append_child().set_name(STR("node"));
 -	CHECK_NODE(doc, STR("<node />"));
 -}
 -
 -#ifndef PUGIXML_NO_STL
 -TEST(document_load_stream)
 -{
 -	pugi::xml_document doc;
 -
 -	std::istringstream iss("<node/>");
 -	CHECK(doc.load(iss));
 -	CHECK_NODE(doc, STR("<node />"));
 -}
 -
 -TEST(document_load_stream_offset)
 -{
 -	pugi::xml_document doc;
 -
 -	std::istringstream iss("<foobar> <node/>");
 -
 -	std::string s;
 -	iss >> s;
 -
 -	CHECK(doc.load(iss));
 -	CHECK_NODE(doc, STR("<node />"));
 -}
 -
 -TEST(document_load_stream_text)
 -{
 -	pugi::xml_document doc;
 -
 -	std::ifstream iss("tests/data/multiline.xml");
 -	CHECK(doc.load(iss));
 -	CHECK_NODE(doc, STR("<node1 /><node2 /><node3 />"));
 -}
 -
 -TEST(document_load_stream_error)
 -{
 -	pugi::xml_document doc;
 -
 -	std::ifstream fs1("filedoesnotexist");
 -	CHECK(doc.load(fs1).status == status_io_error);
 -	
 -#ifndef __DMC__ // Digital Mars CRT does not like 'con' pseudo-file
 -	std::ifstream fs2("con");
 -	CHECK(doc.load(fs2).status == status_io_error);
 -#endif
 -
 -	test_runner::_memory_fail_threshold = 1;
 -	std::istringstream iss("<node/>");
 -	CHECK(doc.load(iss).status == status_out_of_memory);
 -}
 -
 -TEST(document_load_stream_empty)
 -{
 -	std::istringstream iss;
 -
 -	pugi::xml_document doc;
 -	doc.load(iss); // parse result depends on STL implementation
 -	CHECK(!doc.first_child());
 -}
 -
 -TEST(document_load_stream_wide)
 -{
 -	pugi::xml_document doc;
 -
 -	std::basic_istringstream<wchar_t> iss(L"<node/>");
 -	CHECK(doc.load(iss));
 -	CHECK_NODE(doc, STR("<node />"));
 -}
 -#endif
 -
 -TEST(document_load_string)
 -{
 -	pugi::xml_document doc;
 -
 -	CHECK(doc.load(STR("<node/>")));
 -	CHECK_NODE(doc, STR("<node />"));
 -}
 -
 -TEST(document_load_file)
 -{
 -	pugi::xml_document doc;
 -
 -	CHECK(doc.load_file("tests/data/small.xml"));
 -	CHECK_NODE(doc, STR("<node />"));
 -}
 -
 -TEST(document_load_file_empty)
 -{
 -	pugi::xml_document doc;
 -
 -	CHECK(doc.load_file("tests/data/empty.xml"));
 -	CHECK(!doc.first_child());
 -}
 -
 -TEST(document_load_file_large)
 -{
 -	pugi::xml_document doc;
 -
 -	CHECK(doc.load_file("tests/data/large.xml"));
 -
 -	std::basic_string<pugi::char_t> str;
 -	str += STR("<node>");
 -	for (int i = 0; i < 10000; ++i) str += STR("<node />");
 -	str += STR("</node>");
 -
 -	CHECK_NODE(doc, str.c_str());
 -}
 -
 -TEST(document_load_file_error)
 -{
 -	pugi::xml_document doc;
 -
 -	CHECK(doc.load_file("filedoesnotexist").status == status_file_not_found);
 -
 -#ifdef _WIN32
 -#ifndef __DMC__ // Digital Mars CRT does not like 'con' pseudo-file
 -	CHECK(doc.load_file("con").status == status_io_error);
 -#endif
 -#endif
 -
 -	test_runner::_memory_fail_threshold = 1;
 -	CHECK(doc.load_file("tests/data/small.xml").status == status_out_of_memory);
 -}
 -
 -TEST_XML(document_save, "<node/>")
 -{
 -	xml_writer_string writer;
 -
 -	doc.save(writer, STR(""), pugi::format_no_declaration | pugi::format_raw, get_native_encoding());
 -
 -	CHECK(writer.as_string() == STR("<node />"));
 -}
 -
 -#ifndef PUGIXML_NO_STL
 -TEST_XML(document_save_stream, "<node/>")
 -{
 -	std::ostringstream oss;
 -
 -	doc.save(oss, STR(""), pugi::format_no_declaration | pugi::format_raw);
 -
 -	CHECK(oss.str() == "<node />");
 -}
 -
 -TEST_XML(document_save_stream_wide, "<node/>")
 -{
 -	std::basic_ostringstream<wchar_t> oss;
 -
 -	doc.save(oss, STR(""), pugi::format_no_declaration | pugi::format_raw);
 -
 -	CHECK(oss.str() == L"<node />");
 -}
 -#endif
 -
 -TEST_XML(document_save_bom, "<n/>")
 -{
 -	unsigned int flags = format_no_declaration | format_raw | format_write_bom;
 -
 -	// specific encodings
 -	CHECK(test_save_narrow(doc, flags, encoding_utf8, "\xef\xbb\xbf<n />", 8));
 -	CHECK(test_save_narrow(doc, flags, encoding_utf16_be, "\xfe\xff\x00<\x00n\x00 \x00/\x00>", 12));
 -	CHECK(test_save_narrow(doc, flags, encoding_utf16_le, "\xff\xfe<\x00n\x00 \x00/\x00>\x00", 12));
 -	CHECK(test_save_narrow(doc, flags, encoding_utf32_be, "\x00\x00\xfe\xff\x00\x00\x00<\x00\x00\x00n\x00\x00\x00 \x00\x00\x00/\x00\x00\x00>", 24));
 -	CHECK(test_save_narrow(doc, flags, encoding_utf32_le, "\xff\xfe\x00\x00<\x00\x00\x00n\x00\x00\x00 \x00\x00\x00/\x00\x00\x00>\x00\x00\x00", 24));
 -
 -	// encodings synonyms
 -	CHECK(save_narrow(doc, flags, encoding_utf16) == save_narrow(doc, flags, (is_little_endian() ? encoding_utf16_le : encoding_utf16_be)));
 -	CHECK(save_narrow(doc, flags, encoding_utf32) == save_narrow(doc, flags, (is_little_endian() ? encoding_utf32_le : encoding_utf32_be)));
 -
 -	size_t wcharsize = sizeof(wchar_t);
 -	CHECK(save_narrow(doc, flags, encoding_wchar) == save_narrow(doc, flags, (wcharsize == 2 ? encoding_utf16 : encoding_utf32)));
 -}
 -
 -TEST_XML(document_save_declaration, "<node/>")
 -{
 -	xml_writer_string writer;
 -
 -	doc.save(writer, STR(""), pugi::format_default, get_native_encoding());
 -
 -	CHECK(writer.as_string() == STR("<?xml version=\"1.0\"?>\n<node />\n"));
 -}
 -
 -TEST_XML(document_save_declaration_present_first, "<node/>")
 -{
 -	doc.insert_child_before(node_declaration, doc.first_child()).append_attribute(STR("encoding")) = STR("utf8");
 -
 -	xml_writer_string writer;
 -
 -	doc.save(writer, STR(""), pugi::format_default, get_native_encoding());
 -
 -	CHECK(writer.as_string() == STR("<?xml encoding=\"utf8\"?>\n<node />\n"));
 -}
 -
 -TEST_XML(document_save_declaration_present_second, "<node/>")
 -{
 -	doc.insert_child_before(node_declaration, doc.first_child()).append_attribute(STR("encoding")) = STR("utf8");
 -	doc.insert_child_before(node_comment, doc.first_child()).set_value(STR("text"));
 -
 -	xml_writer_string writer;
 -
 -	doc.save(writer, STR(""), pugi::format_default, get_native_encoding());
 -
 -	CHECK(writer.as_string() == STR("<!--text-->\n<?xml encoding=\"utf8\"?>\n<node />\n"));
 -}
 -
 -TEST_XML(document_save_declaration_present_last, "<node/>")
 -{
 -	doc.append_child(node_declaration).append_attribute(STR("encoding")) = STR("utf8");
 -
 -	xml_writer_string writer;
 -
 -	doc.save(writer, STR(""), pugi::format_default, get_native_encoding());
 -
 -	// node writer only looks for declaration before the first element child
 -	CHECK(writer.as_string() == STR("<?xml version=\"1.0\"?>\n<node />\n<?xml encoding=\"utf8\"?>\n"));
 -}
 -
 -TEST_XML(document_save_file, "<node/>")
 -{
 -#ifdef __unix
 -	char path[] = "/tmp/pugiXXXXXX";
 -
 -	int fd = mkstemp(path);
 -	CHECK(fd != -1);
 -#elif defined(__CELLOS_LV2__)
 -	const char* path = ""; // no temporary file support
 -#else
 -	const char* path = tmpnam(0);
 -#endif
 -
 -	CHECK(doc.save_file(path));
 -
 -	CHECK(doc.load_file(path, pugi::parse_default | pugi::parse_declaration));
 -	CHECK_NODE(doc, STR("<?xml version=\"1.0\"?><node />"));
 -
 -	CHECK(unlink(path) == 0);
 -
 -#ifdef __unix
 -	CHECK(close(fd) == 0);
 -#endif
 -}
 -
 -TEST_XML(document_save_file_error, "<node/>")
 -{
 -	CHECK(!doc.save_file("tests/data/unknown/output.xml"));
 -}
 -
 -TEST(document_load_buffer)
 -{
 -	const pugi::char_t text[] = STR("<?xml?><node/>");
 -
 -	pugi::xml_document doc;
 -
 -	CHECK(doc.load_buffer(text, sizeof(text)));
 -	CHECK_NODE(doc, STR("<node />"));
 -}
 -
 -TEST(document_load_buffer_inplace)
 -{
 -	pugi::char_t text[] = STR("<?xml?><node/>");
 -
 -	pugi::xml_document doc;
 -
 -	CHECK(doc.load_buffer_inplace(text, sizeof(text)));
 -	CHECK_NODE(doc, STR("<node />"));
 -}
 -
 -TEST(document_load_buffer_inplace_own)
 -{
 -	allocation_function alloc = get_memory_allocation_function();
 -
 -	size_t size = strlen("<?xml?><node/>") * sizeof(pugi::char_t);
 -
 -	pugi::char_t* text = static_cast<pugi::char_t*>(alloc(size));
 -	CHECK(text);
 -
 -	memcpy(text, STR("<?xml?><node/>"), size);
 -
 -	pugi::xml_document doc;
 -
 -	CHECK(doc.load_buffer_inplace_own(text, size));
 -	CHECK_NODE(doc, STR("<node />"));
 -}
 -
 -TEST(document_parse_result_bool)
 -{
 -	xml_parse_result result;
 -
 -	result.status = status_ok;
 -	CHECK(result);
 -	CHECK(!!result);
 -	CHECK(result == true);
 -
 -	for (int i = 1; i < 20; ++i)
 -	{
 -		result.status = (xml_parse_status)i;
 -		CHECK(!result);
 -		CHECK(result == false);
 -	}
 -}
 -
 -TEST(document_parse_result_description)
 -{
 -	xml_parse_result result;
 -
 -	for (int i = 0; i < 20; ++i)
 -	{
 -		result.status = (xml_parse_status)i;
 -
 -		CHECK(result.description() != 0);
 -		CHECK(result.description()[0] != 0);
 -	}
 -}
 -
 -TEST(document_load_fail)
 -{
 -	xml_document doc;
 -	CHECK(!doc.load(STR("<foo><bar/>")));
 -	CHECK(doc.child(STR("foo")).child(STR("bar")));
 -}
 -
 -inline void check_utftest_document(const xml_document& doc)
 -{
 -	// ascii text
 -	CHECK_STRING(doc.last_child().first_child().name(), STR("English"));
 -
 -	// check that we have parsed some non-ascii text
 -	CHECK((unsigned)doc.last_child().last_child().name()[0] >= 0x80);
 -
 -	// check magic string
 -	const pugi::char_t* v = doc.last_child().child(STR("Heavy")).previous_sibling().child_value();
 -
 -#ifdef PUGIXML_WCHAR_MODE
 -	CHECK(v[0] == 0x4e16 && v[1] == 0x754c && v[2] == 0x6709 && v[3] == 0x5f88 && v[4] == 0x591a && v[5] == 0x8bed && v[6] == 0x8a00);
 -
 -	// last character is a surrogate pair
 -	unsigned int v7 = v[7];
 -	size_t wcharsize = sizeof(wchar_t);
 -
 -	CHECK(wcharsize == 2 ? (v[7] == 0xd852 && v[8] == 0xdf62) : (v7 == 0x24b62));
 -#else
 -	// unicode string
 -	CHECK_STRING(v, "\xe4\xb8\x96\xe7\x95\x8c\xe6\x9c\x89\xe5\xbe\x88\xe5\xa4\x9a\xe8\xaf\xad\xe8\xa8\x80\xf0\xa4\xad\xa2");
 -#endif
 -}
 -
 -TEST(document_load_file_convert_auto)
 -{
 -	const char* files[] =
 -	{
 -		"tests/data/utftest_utf16_be.xml",
 -		"tests/data/utftest_utf16_be_bom.xml",
 -		"tests/data/utftest_utf16_be_nodecl.xml",
 -		"tests/data/utftest_utf16_le.xml",
 -		"tests/data/utftest_utf16_le_bom.xml",
 -		"tests/data/utftest_utf16_le_nodecl.xml",
 -		"tests/data/utftest_utf32_be.xml",
 -		"tests/data/utftest_utf32_be_bom.xml",
 -		"tests/data/utftest_utf32_be_nodecl.xml",
 -		"tests/data/utftest_utf32_le.xml",
 -		"tests/data/utftest_utf32_le_bom.xml",
 -		"tests/data/utftest_utf32_le_nodecl.xml",
 -		"tests/data/utftest_utf8.xml",
 -		"tests/data/utftest_utf8_bom.xml",
 -		"tests/data/utftest_utf8_nodecl.xml"
 -	};
 -
 -	xml_encoding encodings[] =
 -	{
 -		encoding_utf16_be, encoding_utf16_be, encoding_utf16_be,
 -		encoding_utf16_le, encoding_utf16_le, encoding_utf16_le,
 -		encoding_utf32_be, encoding_utf32_be, encoding_utf32_be,
 -		encoding_utf32_le, encoding_utf32_le, encoding_utf32_le,
 -		encoding_utf8, encoding_utf8, encoding_utf8
 -	};
 -
 -	for (unsigned int i = 0; i < sizeof(files) / sizeof(files[0]); ++i)
 -	{
 -		xml_document doc;
 -		xml_parse_result res = doc.load_file(files[i]);
 -
 -		CHECK(res);
 -		CHECK(res.encoding == encodings[i]);
 -		check_utftest_document(doc);
 -	}
 -}
 -
 -TEST(document_load_file_convert_specific)
 -{
 -	const char* files[] =
 -	{
 -		"tests/data/utftest_utf16_be.xml",
 -		"tests/data/utftest_utf16_be_bom.xml",
 -		"tests/data/utftest_utf16_be_nodecl.xml",
 -		"tests/data/utftest_utf16_le.xml",
 -		"tests/data/utftest_utf16_le_bom.xml",
 -		"tests/data/utftest_utf16_le_nodecl.xml",
 -		"tests/data/utftest_utf32_be.xml",
 -		"tests/data/utftest_utf32_be_bom.xml",
 -		"tests/data/utftest_utf32_be_nodecl.xml",
 -		"tests/data/utftest_utf32_le.xml",
 -		"tests/data/utftest_utf32_le_bom.xml",
 -		"tests/data/utftest_utf32_le_nodecl.xml",
 -		"tests/data/utftest_utf8.xml",
 -		"tests/data/utftest_utf8_bom.xml",
 -		"tests/data/utftest_utf8_nodecl.xml"
 -	};
 -
 -	xml_encoding encodings[] =
 -	{
 -		encoding_utf16_be, encoding_utf16_be, encoding_utf16_be,
 -		encoding_utf16_le, encoding_utf16_le, encoding_utf16_le,
 -		encoding_utf32_be, encoding_utf32_be, encoding_utf32_be,
 -		encoding_utf32_le, encoding_utf32_le, encoding_utf32_le,
 -		encoding_utf8, encoding_utf8, encoding_utf8
 -	};
 -
 -	for (unsigned int i = 0; i < sizeof(files) / sizeof(files[0]); ++i)
 -	{
 -		for (unsigned int j = 0; j < sizeof(files) / sizeof(files[0]); ++j)
 -		{
 -			xml_encoding encoding = encodings[j];
 -
 -			xml_document doc;
 -			xml_parse_result res = doc.load_file(files[i], parse_default, encoding);
 -
 -			if (encoding == encodings[i])
 -			{
 -				CHECK(res);
 -				CHECK(res.encoding == encoding);
 -				check_utftest_document(doc);
 -			}
 -			else
 -			{
 -				// should not get past first tag
 -				CHECK(!doc.first_child());
 -			}
 -		}
 -	}
 -}
 -
 -TEST(document_load_file_convert_native_endianness)
 -{
 -	const char* files[2][6] =
 -	{
 -		{
 -			"tests/data/utftest_utf16_be.xml",
 -			"tests/data/utftest_utf16_be_bom.xml",
 -			"tests/data/utftest_utf16_be_nodecl.xml",
 -			"tests/data/utftest_utf32_be.xml",
 -			"tests/data/utftest_utf32_be_bom.xml",
 -			"tests/data/utftest_utf32_be_nodecl.xml",
 -		},
 -		{
 -			"tests/data/utftest_utf16_le.xml",
 -			"tests/data/utftest_utf16_le_bom.xml",
 -			"tests/data/utftest_utf16_le_nodecl.xml",
 -			"tests/data/utftest_utf32_le.xml",
 -			"tests/data/utftest_utf32_le_bom.xml",
 -			"tests/data/utftest_utf32_le_nodecl.xml",
 -		}
 -	};
 -
 -	xml_encoding encodings[] =
 -	{
 -		encoding_utf16, encoding_utf16, encoding_utf16,
 -		encoding_utf32, encoding_utf32, encoding_utf32
 -	};
 -
 -	for (unsigned int i = 0; i < sizeof(files[0]) / sizeof(files[0][0]); ++i)
 -	{
 -		const char* right_file = files[is_little_endian()][i];
 -		const char* wrong_file = files[!is_little_endian()][i];
 -
 -		for (unsigned int j = 0; j < sizeof(encodings) / sizeof(encodings[0]); ++j)
 -		{
 -			xml_encoding encoding = encodings[j];
 -
 -			// check file with right endianness
 -			{
 -				xml_document doc;
 -				xml_parse_result res = doc.load_file(right_file, parse_default, encoding);
 -
 -				if (encoding == encodings[i])
 -				{
 -					CHECK(res);
 -					check_utftest_document(doc);
 -				}
 -				else
 -				{
 -					// should not get past first tag
 -					CHECK(!doc.first_child());
 -				}
 -			}
 -
 -			// check file with wrong endianness
 -			{
 -				xml_document doc;
 -				doc.load_file(wrong_file, parse_default, encoding);
 -				CHECK(!doc.first_child());
 -			}
 -		}
 -	}
 -}
 -
 -static bool load_file_in_memory(const char* path, char*& data, size_t& size)
 -{
 -	FILE* file = fopen(path, "rb");
 -	if (!file) return false;
 -
 -	fseek(file, 0, SEEK_END);
 -	size = (size_t)ftell(file);
 -	fseek(file, 0, SEEK_SET);
 -
 -	data = new char[size];
 -
 -	CHECK(fread(data, 1, size, file) == size);
 -	fclose(file);
 -
 -	return true;
 -}
 -
 -TEST(document_contents_preserve)
 -{
 -	struct file_t
 -	{
 -		const char* path;
 -		xml_encoding encoding;
 -
 -		char* data;
 -		size_t size;
 -	};
 -
 -	file_t files[] =
 -	{
 -		{"tests/data/utftest_utf16_be_clean.xml", encoding_utf16_be, 0, 0},
 -		{"tests/data/utftest_utf16_le_clean.xml", encoding_utf16_le, 0, 0},
 -		{"tests/data/utftest_utf32_be_clean.xml", encoding_utf32_be, 0, 0},
 -		{"tests/data/utftest_utf32_le_clean.xml", encoding_utf32_le, 0, 0},
 -		{"tests/data/utftest_utf8_clean.xml", encoding_utf8, 0, 0}
 -	};
 -
 -	// load files in memory
 -	for (unsigned int i = 0; i < sizeof(files) / sizeof(files[0]); ++i)
 -	{
 -		CHECK(load_file_in_memory(files[i].path, files[i].data, files[i].size));
 -	}
 -
 -	// convert each file to each format and compare bitwise
 -	for (unsigned int src = 0; src < sizeof(files) / sizeof(files[0]); ++src)
 -	{
 -		for (unsigned int dst = 0; dst < sizeof(files) / sizeof(files[0]); ++dst)
 -		{
 -			// parse into document (preserve comments, declaration and whitespace pcdata)
 -			xml_document doc;
 -			CHECK(doc.load_buffer(files[src].data, files[src].size, parse_default | parse_ws_pcdata | parse_declaration | parse_comments));
 -
 -			// compare saved document with the original (raw formatting, without extra declaration, write bom if it was in original file)
 -			CHECK(test_save_narrow(doc, format_raw | format_no_declaration | format_write_bom, files[dst].encoding, files[dst].data, files[dst].size));
 -		}
 -	}
 -
 -	// cleanup
 -	for (unsigned int j = 0; j < sizeof(files) / sizeof(files[0]); ++j)
 -	{
 -		delete[] files[j].data;
 -	}
 -}
 -
 -static bool test_parse_fail(const void* buffer, size_t size, xml_encoding encoding = encoding_utf8)
 -{
 -	// copy buffer to heap (to enable out-of-bounds checks)
 -	void* temp = malloc(size);
 -	memcpy(temp, buffer, size);
 -
 -	// check that this parses without buffer overflows (yielding an error)
 -	xml_document doc;
 -	bool result = doc.load_buffer_inplace(temp, size, parse_default, encoding);
 -
 -	free(temp);
 -
 -	return !result;
 -}
 -
 -TEST(document_convert_invalid_utf8)
 -{
 -	// invalid 1-byte input
 -	CHECK(test_parse_fail("<\xb0", 2));
 -
 -	// invalid 2-byte input
 -	CHECK(test_parse_fail("<\xc0", 2));
 -	CHECK(test_parse_fail("<\xd0", 2));
 -
 -	// invalid 3-byte input
 -	CHECK(test_parse_fail("<\xe2\x80", 3));
 -	CHECK(test_parse_fail("<\xe2", 2));
 -
 -	// invalid 4-byte input
 -	CHECK(test_parse_fail("<\xf2\x97\x98", 4));
 -	CHECK(test_parse_fail("<\xf2\x97", 3));
 -	CHECK(test_parse_fail("<\xf2", 2));
 -
 -	// invalid 5-byte input
 -	CHECK(test_parse_fail("<\xf8", 2));
 -}
 -
 -TEST(document_convert_invalid_utf16)
 -{
 -	// check non-terminated degenerate handling
 -	CHECK(test_parse_fail("\x00<\xda\x1d", 4, encoding_utf16_be));
 -	CHECK(test_parse_fail("<\x00\x1d\xda", 4, encoding_utf16_le));
 -
 -	// check incorrect leading code
 -	CHECK(test_parse_fail("\x00<\xde\x24", 4, encoding_utf16_be));
 -	CHECK(test_parse_fail("<\x00\x24\xde", 4, encoding_utf16_le));
 -}
 -
 -TEST(document_load_buffer_empty)
 -{
 -	xml_encoding encodings[] =
 -	{
 -		encoding_auto,
 -		encoding_utf8,
 -		encoding_utf16_le,
 -		encoding_utf16_be,
 -		encoding_utf16,
 -		encoding_utf32_le,
 -		encoding_utf32_be,
 -		encoding_utf32,
 -		encoding_wchar
 -	};
 -
 -	char buffer[1];
 -
 -	for (unsigned int i = 0; i < sizeof(encodings) / sizeof(encodings[0]); ++i)
 -	{
 -		xml_encoding encoding = encodings[i];
 -
 -		xml_document doc;
 -		CHECK(doc.load_buffer(buffer, 0, parse_default, encoding) && !doc.first_child());
 -		CHECK(doc.load_buffer(0, 0, parse_default, encoding) && !doc.first_child());
 -
 -		CHECK(doc.load_buffer_inplace(buffer, 0, parse_default, encoding) && !doc.first_child());
 -		CHECK(doc.load_buffer_inplace(0, 0, parse_default, encoding) && !doc.first_child());
 -
 -		void* own_buffer = pugi::get_memory_allocation_function()(1);
 -
 -		CHECK(doc.load_buffer_inplace_own(own_buffer, 0, parse_default, encoding) && !doc.first_child());
 -		CHECK(doc.load_buffer_inplace_own(0, 0, parse_default, encoding) && !doc.first_child());
 -	}
 -}
 -
 -TEST(document_progressive_truncation)
 -{
 -	char* original_data;
 -	size_t original_size;
 -
 -	CHECK(load_file_in_memory("tests/data/utftest_utf8.xml", original_data, original_size));
 -
 -	for (size_t i = 1; i < original_size; ++i)
 -	{
 -		char* truncated_data = new char[i];
 -		memcpy(truncated_data, original_data, i);
 -
 -		xml_document doc;
 -		bool result = doc.load_buffer(truncated_data, i);
 -
 -		// some truncate locations are parseable - those that come after declaration, declaration + doctype, declaration + doctype + comment and eof
 -		CHECK(((i - 21) < 3 || (i - 66) < 3 || (i - 95) < 3 || i >= 3325) ? result : !result);
 -
 -		delete[] truncated_data;
 -	}
 -
 -	delete[] original_data;
 -}
 +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_NONSTDC_NO_DEPRECATE 0 + +#include <string.h> // because Borland's STL is braindead, we have to include <string.h> _before_ <string> in order to get memcpy + +#include "common.hpp" + +#include "writer_string.hpp" + +#include <stdio.h> +#include <stdlib.h> + +#include <fstream> +#include <sstream> + +#include <string> + +#ifdef __MINGW32__ +#	include <io.h> // for unlink in C++0x mode +#endif + +#if defined(__CELLOS_LV2__) +#	include <unistd.h> // for unlink +#endif + +TEST(document_create_empty) +{ +	pugi::xml_document doc; +	CHECK_NODE(doc, STR("")); +} + +TEST(document_create) +{ +	pugi::xml_document doc; +	doc.append_child().set_name(STR("node")); +	CHECK_NODE(doc, STR("<node />")); +} + +#ifndef PUGIXML_NO_STL +TEST(document_load_stream) +{ +	pugi::xml_document doc; + +	std::istringstream iss("<node/>"); +	CHECK(doc.load(iss)); +	CHECK_NODE(doc, STR("<node />")); +} + +TEST(document_load_stream_offset) +{ +	pugi::xml_document doc; + +	std::istringstream iss("<foobar> <node/>"); + +	std::string s; +	iss >> s; + +	CHECK(doc.load(iss)); +	CHECK_NODE(doc, STR("<node />")); +} + +TEST(document_load_stream_text) +{ +	pugi::xml_document doc; + +	std::ifstream iss("tests/data/multiline.xml"); +	CHECK(doc.load(iss)); +	CHECK_NODE(doc, STR("<node1 /><node2 /><node3 />")); +} + +TEST(document_load_stream_error) +{ +	pugi::xml_document doc; + +	std::ifstream fs1("filedoesnotexist"); +	CHECK(doc.load(fs1).status == status_io_error); +	 +#ifndef __DMC__ // Digital Mars CRT does not like 'con' pseudo-file +	std::ifstream fs2("con"); +	CHECK(doc.load(fs2).status == status_io_error); +#endif + +	test_runner::_memory_fail_threshold = 1; +	std::istringstream iss("<node/>"); +	CHECK(doc.load(iss).status == status_out_of_memory); +} + +TEST(document_load_stream_empty) +{ +	std::istringstream iss; + +	pugi::xml_document doc; +	doc.load(iss); // parse result depends on STL implementation +	CHECK(!doc.first_child()); +} + +TEST(document_load_stream_wide) +{ +	pugi::xml_document doc; + +	std::basic_istringstream<wchar_t> iss(L"<node/>"); +	CHECK(doc.load(iss)); +	CHECK_NODE(doc, STR("<node />")); +} +#endif + +TEST(document_load_string) +{ +	pugi::xml_document doc; + +	CHECK(doc.load(STR("<node/>"))); +	CHECK_NODE(doc, STR("<node />")); +} + +TEST(document_load_file) +{ +	pugi::xml_document doc; + +	CHECK(doc.load_file("tests/data/small.xml")); +	CHECK_NODE(doc, STR("<node />")); +} + +TEST(document_load_file_empty) +{ +	pugi::xml_document doc; + +	CHECK(doc.load_file("tests/data/empty.xml")); +	CHECK(!doc.first_child()); +} + +TEST(document_load_file_large) +{ +	pugi::xml_document doc; + +	CHECK(doc.load_file("tests/data/large.xml")); + +	std::basic_string<pugi::char_t> str; +	str += STR("<node>"); +	for (int i = 0; i < 10000; ++i) str += STR("<node />"); +	str += STR("</node>"); + +	CHECK_NODE(doc, str.c_str()); +} + +TEST(document_load_file_error) +{ +	pugi::xml_document doc; + +	CHECK(doc.load_file("filedoesnotexist").status == status_file_not_found); + +#ifdef _WIN32 +#ifndef __DMC__ // Digital Mars CRT does not like 'con' pseudo-file +	CHECK(doc.load_file("con").status == status_io_error); +#endif +#endif + +	test_runner::_memory_fail_threshold = 1; +	CHECK(doc.load_file("tests/data/small.xml").status == status_out_of_memory); +} + +TEST_XML(document_save, "<node/>") +{ +	xml_writer_string writer; + +	doc.save(writer, STR(""), pugi::format_no_declaration | pugi::format_raw, get_native_encoding()); + +	CHECK(writer.as_string() == STR("<node />")); +} + +#ifndef PUGIXML_NO_STL +TEST_XML(document_save_stream, "<node/>") +{ +	std::ostringstream oss; + +	doc.save(oss, STR(""), pugi::format_no_declaration | pugi::format_raw); + +	CHECK(oss.str() == "<node />"); +} + +TEST_XML(document_save_stream_wide, "<node/>") +{ +	std::basic_ostringstream<wchar_t> oss; + +	doc.save(oss, STR(""), pugi::format_no_declaration | pugi::format_raw); + +	CHECK(oss.str() == L"<node />"); +} +#endif + +TEST_XML(document_save_bom, "<n/>") +{ +	unsigned int flags = format_no_declaration | format_raw | format_write_bom; + +	// specific encodings +	CHECK(test_save_narrow(doc, flags, encoding_utf8, "\xef\xbb\xbf<n />", 8)); +	CHECK(test_save_narrow(doc, flags, encoding_utf16_be, "\xfe\xff\x00<\x00n\x00 \x00/\x00>", 12)); +	CHECK(test_save_narrow(doc, flags, encoding_utf16_le, "\xff\xfe<\x00n\x00 \x00/\x00>\x00", 12)); +	CHECK(test_save_narrow(doc, flags, encoding_utf32_be, "\x00\x00\xfe\xff\x00\x00\x00<\x00\x00\x00n\x00\x00\x00 \x00\x00\x00/\x00\x00\x00>", 24)); +	CHECK(test_save_narrow(doc, flags, encoding_utf32_le, "\xff\xfe\x00\x00<\x00\x00\x00n\x00\x00\x00 \x00\x00\x00/\x00\x00\x00>\x00\x00\x00", 24)); + +	// encodings synonyms +	CHECK(save_narrow(doc, flags, encoding_utf16) == save_narrow(doc, flags, (is_little_endian() ? encoding_utf16_le : encoding_utf16_be))); +	CHECK(save_narrow(doc, flags, encoding_utf32) == save_narrow(doc, flags, (is_little_endian() ? encoding_utf32_le : encoding_utf32_be))); + +	size_t wcharsize = sizeof(wchar_t); +	CHECK(save_narrow(doc, flags, encoding_wchar) == save_narrow(doc, flags, (wcharsize == 2 ? encoding_utf16 : encoding_utf32))); +} + +TEST_XML(document_save_declaration, "<node/>") +{ +	xml_writer_string writer; + +	doc.save(writer, STR(""), pugi::format_default, get_native_encoding()); + +	CHECK(writer.as_string() == STR("<?xml version=\"1.0\"?>\n<node />\n")); +} + +TEST_XML(document_save_declaration_present_first, "<node/>") +{ +	doc.insert_child_before(node_declaration, doc.first_child()).append_attribute(STR("encoding")) = STR("utf8"); + +	xml_writer_string writer; + +	doc.save(writer, STR(""), pugi::format_default, get_native_encoding()); + +	CHECK(writer.as_string() == STR("<?xml encoding=\"utf8\"?>\n<node />\n")); +} + +TEST_XML(document_save_declaration_present_second, "<node/>") +{ +	doc.insert_child_before(node_declaration, doc.first_child()).append_attribute(STR("encoding")) = STR("utf8"); +	doc.insert_child_before(node_comment, doc.first_child()).set_value(STR("text")); + +	xml_writer_string writer; + +	doc.save(writer, STR(""), pugi::format_default, get_native_encoding()); + +	CHECK(writer.as_string() == STR("<!--text-->\n<?xml encoding=\"utf8\"?>\n<node />\n")); +} + +TEST_XML(document_save_declaration_present_last, "<node/>") +{ +	doc.append_child(node_declaration).append_attribute(STR("encoding")) = STR("utf8"); + +	xml_writer_string writer; + +	doc.save(writer, STR(""), pugi::format_default, get_native_encoding()); + +	// node writer only looks for declaration before the first element child +	CHECK(writer.as_string() == STR("<?xml version=\"1.0\"?>\n<node />\n<?xml encoding=\"utf8\"?>\n")); +} + +TEST_XML(document_save_file, "<node/>") +{ +#ifdef __unix +	char path[] = "/tmp/pugiXXXXXX"; + +	int fd = mkstemp(path); +	CHECK(fd != -1); +#elif defined(__CELLOS_LV2__) +	const char* path = ""; // no temporary file support +#else +	const char* path = tmpnam(0); +#endif + +	CHECK(doc.save_file(path)); + +	CHECK(doc.load_file(path, pugi::parse_default | pugi::parse_declaration)); +	CHECK_NODE(doc, STR("<?xml version=\"1.0\"?><node />")); + +	CHECK(unlink(path) == 0); + +#ifdef __unix +	CHECK(close(fd) == 0); +#endif +} + +TEST_XML(document_save_file_error, "<node/>") +{ +	CHECK(!doc.save_file("tests/data/unknown/output.xml")); +} + +TEST(document_load_buffer) +{ +	const pugi::char_t text[] = STR("<?xml?><node/>"); + +	pugi::xml_document doc; + +	CHECK(doc.load_buffer(text, sizeof(text))); +	CHECK_NODE(doc, STR("<node />")); +} + +TEST(document_load_buffer_inplace) +{ +	pugi::char_t text[] = STR("<?xml?><node/>"); + +	pugi::xml_document doc; + +	CHECK(doc.load_buffer_inplace(text, sizeof(text))); +	CHECK_NODE(doc, STR("<node />")); +} + +TEST(document_load_buffer_inplace_own) +{ +	allocation_function alloc = get_memory_allocation_function(); + +	size_t size = strlen("<?xml?><node/>") * sizeof(pugi::char_t); + +	pugi::char_t* text = static_cast<pugi::char_t*>(alloc(size)); +	CHECK(text); + +	memcpy(text, STR("<?xml?><node/>"), size); + +	pugi::xml_document doc; + +	CHECK(doc.load_buffer_inplace_own(text, size)); +	CHECK_NODE(doc, STR("<node />")); +} + +TEST(document_parse_result_bool) +{ +	xml_parse_result result; + +	result.status = status_ok; +	CHECK(result); +	CHECK(!!result); +	CHECK(result == true); + +	for (int i = 1; i < 20; ++i) +	{ +		result.status = (xml_parse_status)i; +		CHECK(!result); +		CHECK(result == false); +	} +} + +TEST(document_parse_result_description) +{ +	xml_parse_result result; + +	for (int i = 0; i < 20; ++i) +	{ +		result.status = (xml_parse_status)i; + +		CHECK(result.description() != 0); +		CHECK(result.description()[0] != 0); +	} +} + +TEST(document_load_fail) +{ +	xml_document doc; +	CHECK(!doc.load(STR("<foo><bar/>"))); +	CHECK(doc.child(STR("foo")).child(STR("bar"))); +} + +inline void check_utftest_document(const xml_document& doc) +{ +	// ascii text +	CHECK_STRING(doc.last_child().first_child().name(), STR("English")); + +	// check that we have parsed some non-ascii text +	CHECK((unsigned)doc.last_child().last_child().name()[0] >= 0x80); + +	// check magic string +	const pugi::char_t* v = doc.last_child().child(STR("Heavy")).previous_sibling().child_value(); + +#ifdef PUGIXML_WCHAR_MODE +	CHECK(v[0] == 0x4e16 && v[1] == 0x754c && v[2] == 0x6709 && v[3] == 0x5f88 && v[4] == 0x591a && v[5] == 0x8bed && v[6] == 0x8a00); + +	// last character is a surrogate pair +	unsigned int v7 = v[7]; +	size_t wcharsize = sizeof(wchar_t); + +	CHECK(wcharsize == 2 ? (v[7] == 0xd852 && v[8] == 0xdf62) : (v7 == 0x24b62)); +#else +	// unicode string +	CHECK_STRING(v, "\xe4\xb8\x96\xe7\x95\x8c\xe6\x9c\x89\xe5\xbe\x88\xe5\xa4\x9a\xe8\xaf\xad\xe8\xa8\x80\xf0\xa4\xad\xa2"); +#endif +} + +TEST(document_load_file_convert_auto) +{ +	const char* files[] = +	{ +		"tests/data/utftest_utf16_be.xml", +		"tests/data/utftest_utf16_be_bom.xml", +		"tests/data/utftest_utf16_be_nodecl.xml", +		"tests/data/utftest_utf16_le.xml", +		"tests/data/utftest_utf16_le_bom.xml", +		"tests/data/utftest_utf16_le_nodecl.xml", +		"tests/data/utftest_utf32_be.xml", +		"tests/data/utftest_utf32_be_bom.xml", +		"tests/data/utftest_utf32_be_nodecl.xml", +		"tests/data/utftest_utf32_le.xml", +		"tests/data/utftest_utf32_le_bom.xml", +		"tests/data/utftest_utf32_le_nodecl.xml", +		"tests/data/utftest_utf8.xml", +		"tests/data/utftest_utf8_bom.xml", +		"tests/data/utftest_utf8_nodecl.xml" +	}; + +	xml_encoding encodings[] = +	{ +		encoding_utf16_be, encoding_utf16_be, encoding_utf16_be, +		encoding_utf16_le, encoding_utf16_le, encoding_utf16_le, +		encoding_utf32_be, encoding_utf32_be, encoding_utf32_be, +		encoding_utf32_le, encoding_utf32_le, encoding_utf32_le, +		encoding_utf8, encoding_utf8, encoding_utf8 +	}; + +	for (unsigned int i = 0; i < sizeof(files) / sizeof(files[0]); ++i) +	{ +		xml_document doc; +		xml_parse_result res = doc.load_file(files[i]); + +		CHECK(res); +		CHECK(res.encoding == encodings[i]); +		check_utftest_document(doc); +	} +} + +TEST(document_load_file_convert_specific) +{ +	const char* files[] = +	{ +		"tests/data/utftest_utf16_be.xml", +		"tests/data/utftest_utf16_be_bom.xml", +		"tests/data/utftest_utf16_be_nodecl.xml", +		"tests/data/utftest_utf16_le.xml", +		"tests/data/utftest_utf16_le_bom.xml", +		"tests/data/utftest_utf16_le_nodecl.xml", +		"tests/data/utftest_utf32_be.xml", +		"tests/data/utftest_utf32_be_bom.xml", +		"tests/data/utftest_utf32_be_nodecl.xml", +		"tests/data/utftest_utf32_le.xml", +		"tests/data/utftest_utf32_le_bom.xml", +		"tests/data/utftest_utf32_le_nodecl.xml", +		"tests/data/utftest_utf8.xml", +		"tests/data/utftest_utf8_bom.xml", +		"tests/data/utftest_utf8_nodecl.xml" +	}; + +	xml_encoding encodings[] = +	{ +		encoding_utf16_be, encoding_utf16_be, encoding_utf16_be, +		encoding_utf16_le, encoding_utf16_le, encoding_utf16_le, +		encoding_utf32_be, encoding_utf32_be, encoding_utf32_be, +		encoding_utf32_le, encoding_utf32_le, encoding_utf32_le, +		encoding_utf8, encoding_utf8, encoding_utf8 +	}; + +	for (unsigned int i = 0; i < sizeof(files) / sizeof(files[0]); ++i) +	{ +		for (unsigned int j = 0; j < sizeof(files) / sizeof(files[0]); ++j) +		{ +			xml_encoding encoding = encodings[j]; + +			xml_document doc; +			xml_parse_result res = doc.load_file(files[i], parse_default, encoding); + +			if (encoding == encodings[i]) +			{ +				CHECK(res); +				CHECK(res.encoding == encoding); +				check_utftest_document(doc); +			} +			else +			{ +				// should not get past first tag +				CHECK(!doc.first_child()); +			} +		} +	} +} + +TEST(document_load_file_convert_native_endianness) +{ +	const char* files[2][6] = +	{ +		{ +			"tests/data/utftest_utf16_be.xml", +			"tests/data/utftest_utf16_be_bom.xml", +			"tests/data/utftest_utf16_be_nodecl.xml", +			"tests/data/utftest_utf32_be.xml", +			"tests/data/utftest_utf32_be_bom.xml", +			"tests/data/utftest_utf32_be_nodecl.xml", +		}, +		{ +			"tests/data/utftest_utf16_le.xml", +			"tests/data/utftest_utf16_le_bom.xml", +			"tests/data/utftest_utf16_le_nodecl.xml", +			"tests/data/utftest_utf32_le.xml", +			"tests/data/utftest_utf32_le_bom.xml", +			"tests/data/utftest_utf32_le_nodecl.xml", +		} +	}; + +	xml_encoding encodings[] = +	{ +		encoding_utf16, encoding_utf16, encoding_utf16, +		encoding_utf32, encoding_utf32, encoding_utf32 +	}; + +	for (unsigned int i = 0; i < sizeof(files[0]) / sizeof(files[0][0]); ++i) +	{ +		const char* right_file = files[is_little_endian()][i]; +		const char* wrong_file = files[!is_little_endian()][i]; + +		for (unsigned int j = 0; j < sizeof(encodings) / sizeof(encodings[0]); ++j) +		{ +			xml_encoding encoding = encodings[j]; + +			// check file with right endianness +			{ +				xml_document doc; +				xml_parse_result res = doc.load_file(right_file, parse_default, encoding); + +				if (encoding == encodings[i]) +				{ +					CHECK(res); +					check_utftest_document(doc); +				} +				else +				{ +					// should not get past first tag +					CHECK(!doc.first_child()); +				} +			} + +			// check file with wrong endianness +			{ +				xml_document doc; +				doc.load_file(wrong_file, parse_default, encoding); +				CHECK(!doc.first_child()); +			} +		} +	} +} + +static bool load_file_in_memory(const char* path, char*& data, size_t& size) +{ +	FILE* file = fopen(path, "rb"); +	if (!file) return false; + +	fseek(file, 0, SEEK_END); +	size = (size_t)ftell(file); +	fseek(file, 0, SEEK_SET); + +	data = new char[size]; + +	CHECK(fread(data, 1, size, file) == size); +	fclose(file); + +	return true; +} + +TEST(document_contents_preserve) +{ +	struct file_t +	{ +		const char* path; +		xml_encoding encoding; + +		char* data; +		size_t size; +	}; + +	file_t files[] = +	{ +		{"tests/data/utftest_utf16_be_clean.xml", encoding_utf16_be, 0, 0}, +		{"tests/data/utftest_utf16_le_clean.xml", encoding_utf16_le, 0, 0}, +		{"tests/data/utftest_utf32_be_clean.xml", encoding_utf32_be, 0, 0}, +		{"tests/data/utftest_utf32_le_clean.xml", encoding_utf32_le, 0, 0}, +		{"tests/data/utftest_utf8_clean.xml", encoding_utf8, 0, 0} +	}; + +	// load files in memory +	for (unsigned int i = 0; i < sizeof(files) / sizeof(files[0]); ++i) +	{ +		CHECK(load_file_in_memory(files[i].path, files[i].data, files[i].size)); +	} + +	// convert each file to each format and compare bitwise +	for (unsigned int src = 0; src < sizeof(files) / sizeof(files[0]); ++src) +	{ +		for (unsigned int dst = 0; dst < sizeof(files) / sizeof(files[0]); ++dst) +		{ +			// parse into document (preserve comments, declaration and whitespace pcdata) +			xml_document doc; +			CHECK(doc.load_buffer(files[src].data, files[src].size, parse_default | parse_ws_pcdata | parse_declaration | parse_comments)); + +			// compare saved document with the original (raw formatting, without extra declaration, write bom if it was in original file) +			CHECK(test_save_narrow(doc, format_raw | format_no_declaration | format_write_bom, files[dst].encoding, files[dst].data, files[dst].size)); +		} +	} + +	// cleanup +	for (unsigned int j = 0; j < sizeof(files) / sizeof(files[0]); ++j) +	{ +		delete[] files[j].data; +	} +} + +static bool test_parse_fail(const void* buffer, size_t size, xml_encoding encoding = encoding_utf8) +{ +	// copy buffer to heap (to enable out-of-bounds checks) +	void* temp = malloc(size); +	memcpy(temp, buffer, size); + +	// check that this parses without buffer overflows (yielding an error) +	xml_document doc; +	bool result = doc.load_buffer_inplace(temp, size, parse_default, encoding); + +	free(temp); + +	return !result; +} + +TEST(document_convert_invalid_utf8) +{ +	// invalid 1-byte input +	CHECK(test_parse_fail("<\xb0", 2)); + +	// invalid 2-byte input +	CHECK(test_parse_fail("<\xc0", 2)); +	CHECK(test_parse_fail("<\xd0", 2)); + +	// invalid 3-byte input +	CHECK(test_parse_fail("<\xe2\x80", 3)); +	CHECK(test_parse_fail("<\xe2", 2)); + +	// invalid 4-byte input +	CHECK(test_parse_fail("<\xf2\x97\x98", 4)); +	CHECK(test_parse_fail("<\xf2\x97", 3)); +	CHECK(test_parse_fail("<\xf2", 2)); + +	// invalid 5-byte input +	CHECK(test_parse_fail("<\xf8", 2)); +} + +TEST(document_convert_invalid_utf16) +{ +	// check non-terminated degenerate handling +	CHECK(test_parse_fail("\x00<\xda\x1d", 4, encoding_utf16_be)); +	CHECK(test_parse_fail("<\x00\x1d\xda", 4, encoding_utf16_le)); + +	// check incorrect leading code +	CHECK(test_parse_fail("\x00<\xde\x24", 4, encoding_utf16_be)); +	CHECK(test_parse_fail("<\x00\x24\xde", 4, encoding_utf16_le)); +} + +TEST(document_load_buffer_empty) +{ +	xml_encoding encodings[] = +	{ +		encoding_auto, +		encoding_utf8, +		encoding_utf16_le, +		encoding_utf16_be, +		encoding_utf16, +		encoding_utf32_le, +		encoding_utf32_be, +		encoding_utf32, +		encoding_wchar +	}; + +	char buffer[1]; + +	for (unsigned int i = 0; i < sizeof(encodings) / sizeof(encodings[0]); ++i) +	{ +		xml_encoding encoding = encodings[i]; + +		xml_document doc; +		CHECK(doc.load_buffer(buffer, 0, parse_default, encoding) && !doc.first_child()); +		CHECK(doc.load_buffer(0, 0, parse_default, encoding) && !doc.first_child()); + +		CHECK(doc.load_buffer_inplace(buffer, 0, parse_default, encoding) && !doc.first_child()); +		CHECK(doc.load_buffer_inplace(0, 0, parse_default, encoding) && !doc.first_child()); + +		void* own_buffer = pugi::get_memory_allocation_function()(1); + +		CHECK(doc.load_buffer_inplace_own(own_buffer, 0, parse_default, encoding) && !doc.first_child()); +		CHECK(doc.load_buffer_inplace_own(0, 0, parse_default, encoding) && !doc.first_child()); +	} +} + +TEST(document_progressive_truncation) +{ +	char* original_data; +	size_t original_size; + +	CHECK(load_file_in_memory("tests/data/utftest_utf8.xml", original_data, original_size)); + +	for (size_t i = 1; i < original_size; ++i) +	{ +		char* truncated_data = new char[i]; +		memcpy(truncated_data, original_data, i); + +		xml_document doc; +		bool result = doc.load_buffer(truncated_data, i); + +		// some truncate locations are parseable - those that come after declaration, declaration + doctype, declaration + doctype + comment and eof +		CHECK(((i - 21) < 3 || (i - 66) < 3 || (i - 95) < 3 || i >= 3325) ? result : !result); + +		delete[] truncated_data; +	} + +	delete[] original_data; +} diff --git a/tests/test_dom_modify.cpp b/tests/test_dom_modify.cpp index 1e38d95..38cc89e 100644 --- a/tests/test_dom_modify.cpp +++ b/tests/test_dom_modify.cpp @@ -1,659 +1,659 @@ -#include "common.hpp"
 -
 -#include <float.h>
 -
 -TEST_XML(dom_attr_assign, "<node/>")
 -{
 -	xml_node node = doc.child(STR("node"));
 -
 -	node.append_attribute(STR("attr1")) = STR("v1");
 -	xml_attribute() = STR("v1");
 -
 -	node.append_attribute(STR("attr2")) = -2147483647;
 -	node.append_attribute(STR("attr3")) = -2147483647 - 1;
 -	xml_attribute() = -2147483647 - 1;
 -
 -	node.append_attribute(STR("attr4")) = 4294967295u;
 -	node.append_attribute(STR("attr5")) = 4294967294u;
 -	xml_attribute() = 2147483647;
 -
 -	node.append_attribute(STR("attr6")) = 0.5;
 -	xml_attribute() = 0.5;
 -
 -	node.append_attribute(STR("attr7")) = true;
 -	xml_attribute() = true;
 -
 -	CHECK_NODE(node, STR("<node attr1=\"v1\" attr2=\"-2147483647\" attr3=\"-2147483648\" attr4=\"4294967295\" attr5=\"4294967294\" attr6=\"0.5\" attr7=\"true\" />"));
 -}
 -
 -TEST_XML(dom_attr_set_value, "<node/>")
 -{
 -	xml_node node = doc.child(STR("node"));
 -
 -	CHECK(node.append_attribute(STR("attr1")).set_value(STR("v1")));
 -	CHECK(!xml_attribute().set_value(STR("v1")));
 -
 -	CHECK(node.append_attribute(STR("attr2")).set_value(-2147483647));
 -	CHECK(node.append_attribute(STR("attr3")).set_value(-2147483647 - 1));
 -	CHECK(!xml_attribute().set_value(-2147483647));
 -
 -	CHECK(node.append_attribute(STR("attr4")).set_value(4294967295u));
 -	CHECK(node.append_attribute(STR("attr5")).set_value(4294967294u));
 -	CHECK(!xml_attribute().set_value(4294967295u));
 -
 -	CHECK(node.append_attribute(STR("attr6")).set_value(0.5));
 -	CHECK(!xml_attribute().set_value(0.5));
 -
 -	CHECK(node.append_attribute(STR("attr7")).set_value(true));
 -	CHECK(!xml_attribute().set_value(true));
 -
 -	CHECK_NODE(node, STR("<node attr1=\"v1\" attr2=\"-2147483647\" attr3=\"-2147483648\" attr4=\"4294967295\" attr5=\"4294967294\" attr6=\"0.5\" attr7=\"true\" />"));
 -}
 -
 -TEST_XML(dom_node_set_name, "<node>text</node>")
 -{
 -	CHECK(doc.child(STR("node")).set_name(STR("n")));
 -	CHECK(!doc.child(STR("node")).first_child().set_name(STR("n")));
 -	CHECK(!xml_node().set_name(STR("n")));
 -
 -	CHECK_NODE(doc, STR("<n>text</n>"));
 -}
 -
 -TEST_XML(dom_node_set_value, "<node>text</node>")
 -{
 -	CHECK(doc.child(STR("node")).first_child().set_value(STR("no text")));
 -	CHECK(!doc.child(STR("node")).set_value(STR("no text")));
 -	CHECK(!xml_node().set_value(STR("no text")));
 -
 -	CHECK_NODE(doc, STR("<node>no text</node>"));
 -}
 -
 -TEST_XML(dom_node_set_value_allocated, "<node>text</node>")
 -{
 -	CHECK(doc.child(STR("node")).first_child().set_value(STR("no text")));
 -	CHECK(!doc.child(STR("node")).set_value(STR("no text")));
 -	CHECK(!xml_node().set_value(STR("no text")));
 -	CHECK(doc.child(STR("node")).first_child().set_value(STR("no text at all")));
 -
 -	CHECK_NODE(doc, STR("<node>no text at all</node>"));
 -}
 -
 -TEST_XML(dom_node_append_attribute, "<node><child/></node>")
 -{
 -	CHECK(xml_node().append_attribute(STR("a")) == xml_attribute());
 -	CHECK(doc.append_attribute(STR("a")) == xml_attribute());
 -	
 -	xml_attribute a1 = doc.child(STR("node")).append_attribute(STR("a1"));
 -	CHECK(a1);
 -	a1 = STR("v1");
 -
 -	xml_attribute a2 = doc.child(STR("node")).append_attribute(STR("a2"));
 -	CHECK(a2 && a1 != a2);
 -	a2 = STR("v2");
 -
 -	xml_attribute a3 = doc.child(STR("node")).child(STR("child")).append_attribute(STR("a3"));
 -	CHECK(a3 && a1 != a3 && a2 != a3);
 -	a3 = STR("v3");
 -
 -	CHECK_NODE(doc, STR("<node a1=\"v1\" a2=\"v2\"><child a3=\"v3\" /></node>"));
 -}
 -
 -TEST_XML(dom_node_insert_attribute_after, "<node a1='v1'><child a2='v2'/></node>")
 -{
 -	CHECK(xml_node().insert_attribute_after(STR("a"), xml_attribute()) == xml_attribute());
 -
 -	xml_node node = doc.child(STR("node"));
 -	xml_node child = node.child(STR("child"));
 -
 -	xml_attribute a1 = node.attribute(STR("a1"));
 -	xml_attribute a2 = child.attribute(STR("a2"));
 -
 -	CHECK(node.insert_attribute_after(STR("a"), xml_attribute()) == xml_attribute());
 -	CHECK(node.insert_attribute_after(STR("a"), a2) == xml_attribute());
 -	
 -	xml_attribute a3 = node.insert_attribute_after(STR("a3"), a1);
 -	CHECK(a3 && a3 != a2 && a3 != a1);
 -	a3 = STR("v3");
 -
 -	xml_attribute a4 = node.insert_attribute_after(STR("a4"), a1);
 -	CHECK(a4 && a4 != a3 && a4 != a2 && a4 != a1);
 -	a4 = STR("v4");
 -
 -	xml_attribute a5 = node.insert_attribute_after(STR("a5"), a3);
 -	CHECK(a5 && a5 != a4 && a5 != a3 && a5 != a2 && a5 != a1);
 -	a5 = STR("v5");
 -
 -	CHECK(child.insert_attribute_after(STR("a"), a4) == xml_attribute());
 -
 -	CHECK_NODE(doc, STR("<node a1=\"v1\" a4=\"v4\" a3=\"v3\" a5=\"v5\"><child a2=\"v2\" /></node>"));
 -}
 -
 -TEST_XML(dom_node_insert_attribute_before, "<node a1='v1'><child a2='v2'/></node>")
 -{
 -	CHECK(xml_node().insert_attribute_before(STR("a"), xml_attribute()) == xml_attribute());
 -
 -	xml_node node = doc.child(STR("node"));
 -	xml_node child = node.child(STR("child"));
 -
 -	xml_attribute a1 = node.attribute(STR("a1"));
 -	xml_attribute a2 = child.attribute(STR("a2"));
 -
 -	CHECK(node.insert_attribute_before(STR("a"), xml_attribute()) == xml_attribute());
 -	CHECK(node.insert_attribute_before(STR("a"), a2) == xml_attribute());
 -	
 -	xml_attribute a3 = node.insert_attribute_before(STR("a3"), a1);
 -	CHECK(a3 && a3 != a2 && a3 != a1);
 -	a3 = STR("v3");
 -
 -	xml_attribute a4 = node.insert_attribute_before(STR("a4"), a1);
 -	CHECK(a4 && a4 != a3 && a4 != a2 && a4 != a1);
 -	a4 = STR("v4");
 -
 -	xml_attribute a5 = node.insert_attribute_before(STR("a5"), a3);
 -	CHECK(a5 && a5 != a4 && a5 != a3 && a5 != a2 && a5 != a1);
 -	a5 = STR("v5");
 -
 -	CHECK(child.insert_attribute_before(STR("a"), a4) == xml_attribute());
 -
 -	CHECK_NODE(doc, STR("<node a5=\"v5\" a3=\"v3\" a4=\"v4\" a1=\"v1\"><child a2=\"v2\" /></node>"));
 -}
 -
 -TEST_XML(dom_node_append_copy_attribute, "<node a1='v1'><child a2='v2'/><child/></node>")
 -{
 -	CHECK(xml_node().append_copy(xml_attribute()) == xml_attribute());
 -	CHECK(xml_node().append_copy(doc.child(STR("node")).attribute(STR("a1"))) == xml_attribute());
 -	CHECK(doc.append_copy(doc.child(STR("node")).attribute(STR("a1"))) == xml_attribute());
 -	
 -	xml_node node = doc.child(STR("node"));
 -	xml_node child = node.child(STR("child"));
 -
 -	xml_attribute a1 = node.attribute(STR("a1"));
 -	xml_attribute a2 = child.attribute(STR("a2"));
 -
 -	xml_attribute a3 = node.append_copy(a1);
 -	CHECK(a3 && a3 != a2 && a3 != a1);
 -
 -	xml_attribute a4 = node.append_copy(a2);
 -	CHECK(a4 && a4 != a3 && a4 != a2 && a4 != a1);
 -
 -	xml_attribute a5 = node.last_child().append_copy(a1);
 -	CHECK(a5 && a5 != a4 && a5 != a3 && a5 != a2 && a5 != a1);
 -
 -	CHECK_NODE(doc, STR("<node a1=\"v1\" a1=\"v1\" a2=\"v2\"><child a2=\"v2\" /><child a1=\"v1\" /></node>"));
 -
 -	a3.set_name(STR("a3"));
 -	a3 = STR("v3");
 -	
 -	a4.set_name(STR("a4"));
 -	a4 = STR("v4");
 -	
 -	a5.set_name(STR("a5"));
 -	a5 = STR("v5");
 -	
 -	CHECK_NODE(doc, STR("<node a1=\"v1\" a3=\"v3\" a4=\"v4\"><child a2=\"v2\" /><child a5=\"v5\" /></node>"));
 -}
 -
 -TEST_XML(dom_node_insert_copy_after_attribute, "<node a1='v1'><child a2='v2'/></node>")
 -{
 -	CHECK(xml_node().insert_copy_after(xml_attribute(), xml_attribute()) == xml_attribute());
 -
 -	xml_node node = doc.child(STR("node"));
 -	xml_node child = node.child(STR("child"));
 -
 -	xml_attribute a1 = node.attribute(STR("a1"));
 -	xml_attribute a2 = child.attribute(STR("a2"));
 -
 -	CHECK(node.insert_copy_after(a1, xml_attribute()) == xml_attribute());
 -	CHECK(node.insert_copy_after(xml_attribute(), a1) == xml_attribute());
 -	CHECK(node.insert_copy_after(a2, a2) == xml_attribute());
 -	
 -	xml_attribute a3 = node.insert_copy_after(a1, a1);
 -	CHECK(a3 && a3 != a2 && a3 != a1);
 -
 -	xml_attribute a4 = node.insert_copy_after(a2, a1);
 -	CHECK(a4 && a4 != a3 && a4 != a2 && a4 != a1);
 -
 -	xml_attribute a5 = node.insert_copy_after(a4, a1);
 -	CHECK(a5 && a5 != a4 && a5 != a3 && a5 != a2 && a5 != a1);
 -
 -	CHECK(child.insert_copy_after(a4, a4) == xml_attribute());
 -
 -	CHECK_NODE(doc, STR("<node a1=\"v1\" a2=\"v2\" a2=\"v2\" a1=\"v1\"><child a2=\"v2\" /></node>"));
 -
 -	a3.set_name(STR("a3"));
 -	a3 = STR("v3");
 -	
 -	a4.set_name(STR("a4"));
 -	a4 = STR("v4");
 -	
 -	a5.set_name(STR("a5"));
 -	a5 = STR("v5");
 -	
 -	CHECK_NODE(doc, STR("<node a1=\"v1\" a5=\"v5\" a4=\"v4\" a3=\"v3\"><child a2=\"v2\" /></node>"));
 -}
 -
 -TEST_XML(dom_node_insert_copy_before_attribute, "<node a1='v1'><child a2='v2'/></node>")
 -{
 -	CHECK(xml_node().insert_copy_before(xml_attribute(), xml_attribute()) == xml_attribute());
 -
 -	xml_node node = doc.child(STR("node"));
 -	xml_node child = node.child(STR("child"));
 -
 -	xml_attribute a1 = node.attribute(STR("a1"));
 -	xml_attribute a2 = child.attribute(STR("a2"));
 -
 -	CHECK(node.insert_copy_before(a1, xml_attribute()) == xml_attribute());
 -	CHECK(node.insert_copy_before(xml_attribute(), a1) == xml_attribute());
 -	CHECK(node.insert_copy_before(a2, a2) == xml_attribute());
 -	
 -	xml_attribute a3 = node.insert_copy_before(a1, a1);
 -	CHECK(a3 && a3 != a2 && a3 != a1);
 -
 -	xml_attribute a4 = node.insert_copy_before(a2, a1);
 -	CHECK(a4 && a4 != a3 && a4 != a2 && a4 != a1);
 -
 -	xml_attribute a5 = node.insert_copy_before(a4, a1);
 -	CHECK(a5 && a5 != a4 && a5 != a3 && a5 != a2 && a5 != a1);
 -
 -	CHECK(child.insert_copy_before(a4, a4) == xml_attribute());
 -
 -	CHECK_NODE(doc, STR("<node a1=\"v1\" a2=\"v2\" a2=\"v2\" a1=\"v1\"><child a2=\"v2\" /></node>"));
 -
 -	a3.set_name(STR("a3"));
 -	a3 = STR("v3");
 -	
 -	a4.set_name(STR("a4"));
 -	a4 = STR("v4");
 -	
 -	a5.set_name(STR("a5"));
 -	a5 = STR("v5");
 -	
 -	CHECK_NODE(doc, STR("<node a3=\"v3\" a4=\"v4\" a5=\"v5\" a1=\"v1\"><child a2=\"v2\" /></node>"));
 -}
 -
 -TEST_XML(dom_node_remove_attribute, "<node a1='v1' a2='v2' a3='v3'><child a4='v4'/></node>")
 -{
 -	CHECK(!xml_node().remove_attribute(STR("a")));
 -	CHECK(!xml_node().remove_attribute(xml_attribute()));
 -	
 -	xml_node node = doc.child(STR("node"));
 -	xml_node child = node.child(STR("child"));
 -
 -	CHECK(!node.remove_attribute(STR("a")));
 -	CHECK(!node.remove_attribute(xml_attribute()));
 -	CHECK(!node.remove_attribute(child.attribute(STR("a4"))));
 -
 -	CHECK_NODE(doc, STR("<node a1=\"v1\" a2=\"v2\" a3=\"v3\"><child a4=\"v4\" /></node>"));
 -
 -	CHECK(node.remove_attribute(STR("a1")));
 -	CHECK(node.remove_attribute(node.attribute(STR("a3"))));
 -	CHECK(child.remove_attribute(STR("a4")));
 -
 -	CHECK_NODE(doc, STR("<node a2=\"v2\"><child /></node>"));
 -}
 -
 -TEST_XML(dom_node_append_child, "<node>foo<child/></node>")
 -{
 -	CHECK(xml_node().append_child() == xml_node());
 -	CHECK(doc.child(STR("node")).first_child().append_child() == xml_node());
 -	CHECK(doc.append_child(node_document) == xml_node());
 -	CHECK(doc.append_child(node_null) == xml_node());
 -	
 -	xml_node n1 = doc.child(STR("node")).append_child();
 -	CHECK(n1);
 -	CHECK(n1.set_name(STR("n1")));
 -
 -	xml_node n2 = doc.child(STR("node")).append_child();
 -	CHECK(n2 && n1 != n2);
 -	CHECK(n2.set_name(STR("n2")));
 -
 -	xml_node n3 = doc.child(STR("node")).child(STR("child")).append_child(node_pcdata);
 -	CHECK(n3 && n1 != n3 && n2 != n3);
 -	CHECK(n3.set_value(STR("n3")));
 -	
 -	xml_node n4 = doc.append_child(node_comment);
 -	CHECK(n4 && n1 != n4 && n2 != n4 && n3 != n4);
 -	CHECK(n4.set_value(STR("n4")));
 -
 -	CHECK_NODE(doc, STR("<node>foo<child>n3</child><n1 /><n2 /></node><!--n4-->"));
 -}
 -
 -TEST_XML(dom_node_insert_child_after, "<node>foo<child/></node>")
 -{
 -	CHECK(xml_node().insert_child_after(node_element, xml_node()) == xml_node());
 -	CHECK(doc.child(STR("node")).first_child().insert_child_after(node_element, xml_node()) == xml_node());
 -	CHECK(doc.insert_child_after(node_document, xml_node()) == xml_node());
 -	CHECK(doc.insert_child_after(node_null, xml_node()) == xml_node());
 -
 -	xml_node node = doc.child(STR("node"));
 -	xml_node child = node.child(STR("child"));
 -
 -	CHECK(node.insert_child_after(node_element, node) == xml_node());
 -	CHECK(child.insert_child_after(node_element, node) == xml_node());
 -	
 -	xml_node n1 = node.insert_child_after(node_element, child);
 -	CHECK(n1 && n1 != node && n1 != child);
 -	CHECK(n1.set_name(STR("n1")));
 -
 -	xml_node n2 = node.insert_child_after(node_element, child);
 -	CHECK(n2 && n2 != node && n2 != child && n2 != n1);
 -	CHECK(n2.set_name(STR("n2")));
 -
 -	xml_node n3 = node.insert_child_after(node_pcdata, n2);
 -	CHECK(n3 && n3 != node && n3 != child && n3 != n1 && n3 != n2);
 -	CHECK(n3.set_value(STR("n3")));
 -
 -	xml_node n4 = node.insert_child_after(node_pi, node.first_child());
 -	CHECK(n4 && n4 != node && n4 != child && n4 != n1 && n4 != n2 && n4 != n3);
 -	CHECK(n4.set_name(STR("n4")));
 -
 -	CHECK(child.insert_child_after(node_element, n3) == xml_node());
 -
 -	CHECK_NODE(doc, STR("<node>foo<?n4?><child /><n2 />n3<n1 /></node>"));
 -}
 -
 -TEST_XML(dom_node_insert_child_before, "<node>foo<child/></node>")
 -{
 -	CHECK(xml_node().insert_child_before(node_element, xml_node()) == xml_node());
 -	CHECK(doc.child(STR("node")).first_child().insert_child_before(node_element, xml_node()) == xml_node());
 -	CHECK(doc.insert_child_before(node_document, xml_node()) == xml_node());
 -	CHECK(doc.insert_child_before(node_null, xml_node()) == xml_node());
 -
 -	xml_node node = doc.child(STR("node"));
 -	xml_node child = node.child(STR("child"));
 -
 -	CHECK(node.insert_child_before(node_element, node) == xml_node());
 -	CHECK(child.insert_child_before(node_element, node) == xml_node());
 -	
 -	xml_node n1 = node.insert_child_before(node_element, child);
 -	CHECK(n1 && n1 != node && n1 != child);
 -	CHECK(n1.set_name(STR("n1")));
 -
 -	xml_node n2 = node.insert_child_before(node_element, child);
 -	CHECK(n2 && n2 != node && n2 != child && n2 != n1);
 -	CHECK(n2.set_name(STR("n2")));
 -
 -	xml_node n3 = node.insert_child_before(node_pcdata, n2);
 -	CHECK(n3 && n3 != node && n3 != child && n3 != n1 && n3 != n2);
 -	CHECK(n3.set_value(STR("n3")));
 -
 -	xml_node n4 = node.insert_child_before(node_pi, node.first_child());
 -	CHECK(n4 && n4 != node && n4 != child && n4 != n1 && n4 != n2 && n4 != n3);
 -	CHECK(n4.set_name(STR("n4")));
 -
 -	CHECK(child.insert_child_before(node_element, n3) == xml_node());
 -
 -	CHECK_NODE(doc, STR("<node><?n4?>foo<n1 />n3<n2 /><child /></node>"));
 -}
 -
 -TEST_XML(dom_node_remove_child, "<node><n1/><n2/><n3/><child><n4/></child></node>")
 -{
 -	CHECK(!xml_node().remove_child(STR("a")));
 -	CHECK(!xml_node().remove_child(xml_node()));
 -	
 -	xml_node node = doc.child(STR("node"));
 -	xml_node child = node.child(STR("child"));
 -
 -	CHECK(!node.remove_child(STR("a")));
 -	CHECK(!node.remove_child(xml_node()));
 -	CHECK(!node.remove_child(child.child(STR("n4"))));
 -
 -	CHECK_NODE(doc, STR("<node><n1 /><n2 /><n3 /><child><n4 /></child></node>"));
 -
 -	CHECK(node.remove_child(STR("n1")));
 -	CHECK(node.remove_child(node.child(STR("n3"))));
 -	CHECK(child.remove_child(STR("n4")));
 -
 -	CHECK_NODE(doc, STR("<node><n2 /><child /></node>"));
 -}
 -
 -TEST_XML(dom_node_remove_child_complex, "<node id='1'><n1 id1='1' id2='2'/><n2/><n3/><child><n4/></child></node>")
 -{
 -	doc.child(STR("node")).remove_child(STR("n1"));
 -
 -	CHECK_NODE(doc, STR("<node id=\"1\"><n2 /><n3 /><child><n4 /></child></node>"));
 -
 -	CHECK(doc.remove_child(STR("node")));
 -
 -	CHECK_NODE(doc, STR(""));
 -}
 -
 -TEST_XML(dom_node_remove_child_complex_allocated, "<node id='1'><n1 id1='1' id2='2'/><n2/><n3/><child><n4/></child></node>")
 -{
 -	doc.append_copy(doc.child(STR("node")));
 -
 -	CHECK(doc.remove_child(STR("node")));
 -	CHECK(doc.remove_child(STR("node")));
 -
 -	CHECK_NODE(doc, STR(""));
 -}
 -
 -TEST_XML(dom_node_append_copy, "<node>foo<child/></node>")
 -{
 -	CHECK(xml_node().append_copy(xml_node()) == xml_node());
 -	CHECK(doc.child(STR("node")).first_child().append_copy(doc.child(STR("node"))) == xml_node());
 -	CHECK(doc.append_copy(doc) == xml_node());
 -	CHECK(doc.append_copy(xml_node()) == xml_node());
 -	
 -	xml_node n1 = doc.child(STR("node")).append_copy(doc.child(STR("node")).first_child());
 -	CHECK(n1);
 -	CHECK_STRING(n1.value(), STR("foo"));
 -	CHECK_NODE(doc, STR("<node>foo<child />foo</node>"));
 -
 -	xml_node n2 = doc.child(STR("node")).append_copy(doc.child(STR("node")).child(STR("child")));
 -	CHECK(n2 && n2 != n1);
 -	CHECK_STRING(n2.name(), STR("child"));
 -	CHECK_NODE(doc, STR("<node>foo<child />foo<child /></node>"));
 -
 -	xml_node n3 = doc.child(STR("node")).child(STR("child")).append_copy(doc.child(STR("node")).first_child());
 -	CHECK(n3 && n3 != n1 && n3 != n2);
 -	CHECK_STRING(n3.value(), STR("foo"));
 -	CHECK_NODE(doc, STR("<node>foo<child>foo</child>foo<child /></node>"));
 -}
 -
 -TEST_XML(dom_node_insert_copy_after, "<node>foo<child/></node>")
 -{
 -	CHECK(xml_node().insert_copy_after(xml_node(), xml_node()) == xml_node());
 -	CHECK(doc.child(STR("node")).first_child().insert_copy_after(doc.child(STR("node")), doc.child(STR("node"))) == xml_node());
 -	CHECK(doc.insert_copy_after(doc, doc) == xml_node());
 -	CHECK(doc.insert_copy_after(xml_node(), doc.child(STR("node"))) == xml_node());
 -	CHECK(doc.insert_copy_after(doc.child(STR("node")), xml_node()) == xml_node());
 -	
 -	xml_node n1 = doc.child(STR("node")).insert_copy_after(doc.child(STR("node")).child(STR("child")), doc.child(STR("node")).first_child());
 -	CHECK(n1);
 -	CHECK_STRING(n1.name(), STR("child"));
 -	CHECK_NODE(doc, STR("<node>foo<child /><child /></node>"));
 -
 -	xml_node n2 = doc.child(STR("node")).insert_copy_after(doc.child(STR("node")).first_child(), doc.child(STR("node")).last_child());
 -	CHECK(n2 && n2 != n1);
 -	CHECK_STRING(n2.value(), STR("foo"));
 -	CHECK_NODE(doc, STR("<node>foo<child /><child />foo</node>"));
 -
 -	xml_node n3 = doc.child(STR("node")).insert_copy_after(doc.child(STR("node")).first_child(), doc.child(STR("node")).first_child());
 -	CHECK(n3 && n3 != n1 && n3 != n2);
 -	CHECK_STRING(n3.value(), STR("foo"));
 -	CHECK_NODE(doc, STR("<node>foofoo<child /><child />foo</node>"));
 -}
 -
 -TEST_XML(dom_node_insert_copy_before, "<node>foo<child/></node>")
 -{
 -	CHECK(xml_node().insert_copy_before(xml_node(), xml_node()) == xml_node());
 -	CHECK(doc.child(STR("node")).first_child().insert_copy_before(doc.child(STR("node")), doc.child(STR("node"))) == xml_node());
 -	CHECK(doc.insert_copy_before(doc, doc) == xml_node());
 -	CHECK(doc.insert_copy_before(xml_node(), doc.child(STR("node"))) == xml_node());
 -	CHECK(doc.insert_copy_before(doc.child(STR("node")), xml_node()) == xml_node());
 -	
 -	xml_node n1 = doc.child(STR("node")).insert_copy_before(doc.child(STR("node")).child(STR("child")), doc.child(STR("node")).first_child());
 -	CHECK(n1);
 -	CHECK_STRING(n1.name(), STR("child"));
 -	CHECK_NODE(doc, STR("<node><child />foo<child /></node>"));
 -
 -	xml_node n2 = doc.child(STR("node")).insert_copy_before(doc.child(STR("node")).first_child(), doc.child(STR("node")).last_child());
 -	CHECK(n2 && n2 != n1);
 -	CHECK_STRING(n2.name(), STR("child"));
 -	CHECK_NODE(doc, STR("<node><child />foo<child /><child /></node>"));
 -
 -	xml_node n3 = doc.child(STR("node")).insert_copy_before(doc.child(STR("node")).first_child().next_sibling(), doc.child(STR("node")).first_child());
 -	CHECK(n3 && n3 != n1 && n3 != n2);
 -	CHECK_STRING(n3.value(), STR("foo"));
 -	CHECK_NODE(doc, STR("<node>foo<child />foo<child /><child /></node>"));
 -}
 -
 -TEST_XML(dom_node_copy_recursive, "<node>foo<child/></node>")
 -{
 -	doc.child(STR("node")).append_copy(doc.child(STR("node")));
 -	CHECK_NODE(doc, STR("<node>foo<child /><node>foo<child /></node></node>"));
 -}
 -
 -TEST_XML(dom_node_copy_crossdoc, "<node/>")
 -{
 -	xml_document newdoc;
 -	newdoc.append_copy(doc.child(STR("node")));
 -	CHECK_NODE(doc, STR("<node />"));
 -	CHECK_NODE(newdoc, STR("<node />"));
 -}
 -
 -TEST_XML_FLAGS(dom_node_copy_types, "<?xml version='1.0'?><root><?pi value?><!--comment--><node id='1'>pcdata<![CDATA[cdata]]></node></root>", parse_default | parse_pi | parse_comments | parse_declaration)
 -{
 -	doc.append_copy(doc.child(STR("root")));
 -	CHECK_NODE(doc, STR("<?xml version=\"1.0\"?><root><?pi value?><!--comment--><node id=\"1\">pcdata<![CDATA[cdata]]></node></root><root><?pi value?><!--comment--><node id=\"1\">pcdata<![CDATA[cdata]]></node></root>"));
 -
 -	doc.insert_copy_before(doc.first_child(), doc.first_child());
 -	CHECK_NODE(doc, STR("<?xml version=\"1.0\"?><?xml version=\"1.0\"?><root><?pi value?><!--comment--><node id=\"1\">pcdata<![CDATA[cdata]]></node></root><root><?pi value?><!--comment--><node id=\"1\">pcdata<![CDATA[cdata]]></node></root>"));
 -}
 -
 -TEST_XML(dom_attr_assign_large_number, "<node attr1='' attr2='' />")
 -{
 -	xml_node node = doc.child(STR("node"));
 -
 -	node.attribute(STR("attr1")) = FLT_MAX;
 -	node.attribute(STR("attr2")) = DBL_MAX;
 -
 -	CHECK(test_node(node, STR("<node attr1=\"3.40282e+038\" attr2=\"1.79769e+308\" />"), STR(""), pugi::format_raw) ||
 -		  test_node(node, STR("<node attr1=\"3.40282e+38\" attr2=\"1.79769e+308\" />"), STR(""), pugi::format_raw));
 -}
 -
 -TEST(dom_node_declaration_name)
 -{
 -	xml_document doc;
 -	doc.append_child(node_declaration);
 -
 -	// name 'xml' is auto-assigned
 -	CHECK(doc.first_child().type() == node_declaration);
 -	CHECK_STRING(doc.first_child().name(), STR("xml"));
 -
 -	doc.insert_child_after(node_declaration, doc.first_child());
 -	doc.insert_child_before(node_declaration, doc.first_child());
 -
 -	CHECK_NODE(doc, STR("<?xml?><?xml?><?xml?>"));
 -}
 -
 -TEST(dom_node_declaration_top_level)
 -{
 -	xml_document doc;
 -	doc.append_child().set_name(STR("node"));
 -
 -	xml_node node = doc.first_child();
 -	node.append_child(node_pcdata).set_value(STR("text"));
 -
 -	CHECK(node.insert_child_before(node_declaration, node.first_child()) == xml_node());
 -	CHECK(node.insert_child_after(node_declaration, node.first_child()) == xml_node());
 -	CHECK(node.append_child(node_declaration) == xml_node());
 -
 -	CHECK_NODE(doc, STR("<node>text</node>"));
 -
 -	CHECK(doc.insert_child_before(node_declaration, node));
 -	CHECK(doc.insert_child_after(node_declaration, node));
 -	CHECK(doc.append_child(node_declaration));
 -
 -	CHECK_NODE(doc, STR("<?xml?><node>text</node><?xml?><?xml?>"));
 -}
 -
 -TEST(dom_node_declaration_copy)
 -{
 -	xml_document doc;
 -	doc.append_child(node_declaration);
 -
 -	doc.append_child().set_name(STR("node"));
 -
 -	doc.last_child().append_copy(doc.first_child());
 -
 -	CHECK_NODE(doc, STR("<?xml?><node />"));
 -}
 -
 -TEST(dom_string_out_of_memory)
 -{
 -	unsigned int length = 65536;
 -
 -	char_t* string = new char_t[length + 1];
 -	for (unsigned int i = 0; i < length; ++i) string[i] = 'a';
 -	string[length] = 0;
 -
 -	xml_document doc;
 -	xml_node node = doc.append_child();
 -	xml_attribute attr = node.append_attribute(STR("a"));
 -	xml_node text = node.append_child(node_pcdata);
 -
 -	// no value => long value
 -	test_runner::_memory_fail_threshold = 32;
 -
 -	CHECK(!node.set_name(string));
 -	CHECK(!text.set_value(string));
 -	CHECK(!attr.set_name(string));
 -	CHECK(!attr.set_value(string));
 -
 -	// set some names/values
 -	test_runner::_memory_fail_threshold = 0;
 -
 -	node.set_name(STR("n"));
 -	attr.set_value(STR("v"));
 -	text.set_value(STR("t"));
 -
 -	// some value => long value
 -	test_runner::_memory_fail_threshold = 32;
 -
 -	CHECK(!node.set_name(string));
 -	CHECK(!text.set_value(string));
 -	CHECK(!attr.set_name(string));
 -	CHECK(!attr.set_value(string));
 -
 -	// check that original state was preserved
 -	test_runner::_memory_fail_threshold = 0;
 -
 -	CHECK_NODE(doc, STR("<n a=\"v\">t</n>"));
 -}
 -
 -TEST(dom_node_out_of_memory)
 -{
 -	test_runner::_memory_fail_threshold = 65536;
 -
 -	// exhaust memory limit
 -	xml_document doc;
 -
 -	xml_node n = doc.append_child();
 -	CHECK(n.set_name(STR("n")));
 -
 -	xml_attribute a = n.append_attribute(STR("a"));
 -	CHECK(a);
 -
 -	while (n.append_child(node_comment) || n.append_attribute(STR("b")))
 -	{
 -		// nop
 -	}
 -
 -	// verify all node modification operations
 -	CHECK(!n.append_child());
 -	CHECK(!n.insert_child_after(node_element, n.first_child()));
 -	CHECK(!n.insert_child_before(node_element, n.first_child()));
 -	CHECK(!n.append_attribute(STR("")));
 -	CHECK(!n.insert_attribute_after(STR(""), a));
 -	CHECK(!n.insert_attribute_before(STR(""), a));
 -
 -	// verify node copy operations
 -	CHECK(!n.append_copy(n.first_child()));
 -	CHECK(!n.insert_copy_after(n.first_child(), n.first_child()));
 -	CHECK(!n.insert_copy_before(n.first_child(), n.first_child()));
 -	CHECK(!n.append_copy(a));
 -	CHECK(!n.insert_copy_after(a, a));
 -	CHECK(!n.insert_copy_before(a, a));
 -}
 +#include "common.hpp" + +#include <float.h> + +TEST_XML(dom_attr_assign, "<node/>") +{ +	xml_node node = doc.child(STR("node")); + +	node.append_attribute(STR("attr1")) = STR("v1"); +	xml_attribute() = STR("v1"); + +	node.append_attribute(STR("attr2")) = -2147483647; +	node.append_attribute(STR("attr3")) = -2147483647 - 1; +	xml_attribute() = -2147483647 - 1; + +	node.append_attribute(STR("attr4")) = 4294967295u; +	node.append_attribute(STR("attr5")) = 4294967294u; +	xml_attribute() = 2147483647; + +	node.append_attribute(STR("attr6")) = 0.5; +	xml_attribute() = 0.5; + +	node.append_attribute(STR("attr7")) = true; +	xml_attribute() = true; + +	CHECK_NODE(node, STR("<node attr1=\"v1\" attr2=\"-2147483647\" attr3=\"-2147483648\" attr4=\"4294967295\" attr5=\"4294967294\" attr6=\"0.5\" attr7=\"true\" />")); +} + +TEST_XML(dom_attr_set_value, "<node/>") +{ +	xml_node node = doc.child(STR("node")); + +	CHECK(node.append_attribute(STR("attr1")).set_value(STR("v1"))); +	CHECK(!xml_attribute().set_value(STR("v1"))); + +	CHECK(node.append_attribute(STR("attr2")).set_value(-2147483647)); +	CHECK(node.append_attribute(STR("attr3")).set_value(-2147483647 - 1)); +	CHECK(!xml_attribute().set_value(-2147483647)); + +	CHECK(node.append_attribute(STR("attr4")).set_value(4294967295u)); +	CHECK(node.append_attribute(STR("attr5")).set_value(4294967294u)); +	CHECK(!xml_attribute().set_value(4294967295u)); + +	CHECK(node.append_attribute(STR("attr6")).set_value(0.5)); +	CHECK(!xml_attribute().set_value(0.5)); + +	CHECK(node.append_attribute(STR("attr7")).set_value(true)); +	CHECK(!xml_attribute().set_value(true)); + +	CHECK_NODE(node, STR("<node attr1=\"v1\" attr2=\"-2147483647\" attr3=\"-2147483648\" attr4=\"4294967295\" attr5=\"4294967294\" attr6=\"0.5\" attr7=\"true\" />")); +} + +TEST_XML(dom_node_set_name, "<node>text</node>") +{ +	CHECK(doc.child(STR("node")).set_name(STR("n"))); +	CHECK(!doc.child(STR("node")).first_child().set_name(STR("n"))); +	CHECK(!xml_node().set_name(STR("n"))); + +	CHECK_NODE(doc, STR("<n>text</n>")); +} + +TEST_XML(dom_node_set_value, "<node>text</node>") +{ +	CHECK(doc.child(STR("node")).first_child().set_value(STR("no text"))); +	CHECK(!doc.child(STR("node")).set_value(STR("no text"))); +	CHECK(!xml_node().set_value(STR("no text"))); + +	CHECK_NODE(doc, STR("<node>no text</node>")); +} + +TEST_XML(dom_node_set_value_allocated, "<node>text</node>") +{ +	CHECK(doc.child(STR("node")).first_child().set_value(STR("no text"))); +	CHECK(!doc.child(STR("node")).set_value(STR("no text"))); +	CHECK(!xml_node().set_value(STR("no text"))); +	CHECK(doc.child(STR("node")).first_child().set_value(STR("no text at all"))); + +	CHECK_NODE(doc, STR("<node>no text at all</node>")); +} + +TEST_XML(dom_node_append_attribute, "<node><child/></node>") +{ +	CHECK(xml_node().append_attribute(STR("a")) == xml_attribute()); +	CHECK(doc.append_attribute(STR("a")) == xml_attribute()); +	 +	xml_attribute a1 = doc.child(STR("node")).append_attribute(STR("a1")); +	CHECK(a1); +	a1 = STR("v1"); + +	xml_attribute a2 = doc.child(STR("node")).append_attribute(STR("a2")); +	CHECK(a2 && a1 != a2); +	a2 = STR("v2"); + +	xml_attribute a3 = doc.child(STR("node")).child(STR("child")).append_attribute(STR("a3")); +	CHECK(a3 && a1 != a3 && a2 != a3); +	a3 = STR("v3"); + +	CHECK_NODE(doc, STR("<node a1=\"v1\" a2=\"v2\"><child a3=\"v3\" /></node>")); +} + +TEST_XML(dom_node_insert_attribute_after, "<node a1='v1'><child a2='v2'/></node>") +{ +	CHECK(xml_node().insert_attribute_after(STR("a"), xml_attribute()) == xml_attribute()); + +	xml_node node = doc.child(STR("node")); +	xml_node child = node.child(STR("child")); + +	xml_attribute a1 = node.attribute(STR("a1")); +	xml_attribute a2 = child.attribute(STR("a2")); + +	CHECK(node.insert_attribute_after(STR("a"), xml_attribute()) == xml_attribute()); +	CHECK(node.insert_attribute_after(STR("a"), a2) == xml_attribute()); +	 +	xml_attribute a3 = node.insert_attribute_after(STR("a3"), a1); +	CHECK(a3 && a3 != a2 && a3 != a1); +	a3 = STR("v3"); + +	xml_attribute a4 = node.insert_attribute_after(STR("a4"), a1); +	CHECK(a4 && a4 != a3 && a4 != a2 && a4 != a1); +	a4 = STR("v4"); + +	xml_attribute a5 = node.insert_attribute_after(STR("a5"), a3); +	CHECK(a5 && a5 != a4 && a5 != a3 && a5 != a2 && a5 != a1); +	a5 = STR("v5"); + +	CHECK(child.insert_attribute_after(STR("a"), a4) == xml_attribute()); + +	CHECK_NODE(doc, STR("<node a1=\"v1\" a4=\"v4\" a3=\"v3\" a5=\"v5\"><child a2=\"v2\" /></node>")); +} + +TEST_XML(dom_node_insert_attribute_before, "<node a1='v1'><child a2='v2'/></node>") +{ +	CHECK(xml_node().insert_attribute_before(STR("a"), xml_attribute()) == xml_attribute()); + +	xml_node node = doc.child(STR("node")); +	xml_node child = node.child(STR("child")); + +	xml_attribute a1 = node.attribute(STR("a1")); +	xml_attribute a2 = child.attribute(STR("a2")); + +	CHECK(node.insert_attribute_before(STR("a"), xml_attribute()) == xml_attribute()); +	CHECK(node.insert_attribute_before(STR("a"), a2) == xml_attribute()); +	 +	xml_attribute a3 = node.insert_attribute_before(STR("a3"), a1); +	CHECK(a3 && a3 != a2 && a3 != a1); +	a3 = STR("v3"); + +	xml_attribute a4 = node.insert_attribute_before(STR("a4"), a1); +	CHECK(a4 && a4 != a3 && a4 != a2 && a4 != a1); +	a4 = STR("v4"); + +	xml_attribute a5 = node.insert_attribute_before(STR("a5"), a3); +	CHECK(a5 && a5 != a4 && a5 != a3 && a5 != a2 && a5 != a1); +	a5 = STR("v5"); + +	CHECK(child.insert_attribute_before(STR("a"), a4) == xml_attribute()); + +	CHECK_NODE(doc, STR("<node a5=\"v5\" a3=\"v3\" a4=\"v4\" a1=\"v1\"><child a2=\"v2\" /></node>")); +} + +TEST_XML(dom_node_append_copy_attribute, "<node a1='v1'><child a2='v2'/><child/></node>") +{ +	CHECK(xml_node().append_copy(xml_attribute()) == xml_attribute()); +	CHECK(xml_node().append_copy(doc.child(STR("node")).attribute(STR("a1"))) == xml_attribute()); +	CHECK(doc.append_copy(doc.child(STR("node")).attribute(STR("a1"))) == xml_attribute()); +	 +	xml_node node = doc.child(STR("node")); +	xml_node child = node.child(STR("child")); + +	xml_attribute a1 = node.attribute(STR("a1")); +	xml_attribute a2 = child.attribute(STR("a2")); + +	xml_attribute a3 = node.append_copy(a1); +	CHECK(a3 && a3 != a2 && a3 != a1); + +	xml_attribute a4 = node.append_copy(a2); +	CHECK(a4 && a4 != a3 && a4 != a2 && a4 != a1); + +	xml_attribute a5 = node.last_child().append_copy(a1); +	CHECK(a5 && a5 != a4 && a5 != a3 && a5 != a2 && a5 != a1); + +	CHECK_NODE(doc, STR("<node a1=\"v1\" a1=\"v1\" a2=\"v2\"><child a2=\"v2\" /><child a1=\"v1\" /></node>")); + +	a3.set_name(STR("a3")); +	a3 = STR("v3"); +	 +	a4.set_name(STR("a4")); +	a4 = STR("v4"); +	 +	a5.set_name(STR("a5")); +	a5 = STR("v5"); +	 +	CHECK_NODE(doc, STR("<node a1=\"v1\" a3=\"v3\" a4=\"v4\"><child a2=\"v2\" /><child a5=\"v5\" /></node>")); +} + +TEST_XML(dom_node_insert_copy_after_attribute, "<node a1='v1'><child a2='v2'/></node>") +{ +	CHECK(xml_node().insert_copy_after(xml_attribute(), xml_attribute()) == xml_attribute()); + +	xml_node node = doc.child(STR("node")); +	xml_node child = node.child(STR("child")); + +	xml_attribute a1 = node.attribute(STR("a1")); +	xml_attribute a2 = child.attribute(STR("a2")); + +	CHECK(node.insert_copy_after(a1, xml_attribute()) == xml_attribute()); +	CHECK(node.insert_copy_after(xml_attribute(), a1) == xml_attribute()); +	CHECK(node.insert_copy_after(a2, a2) == xml_attribute()); +	 +	xml_attribute a3 = node.insert_copy_after(a1, a1); +	CHECK(a3 && a3 != a2 && a3 != a1); + +	xml_attribute a4 = node.insert_copy_after(a2, a1); +	CHECK(a4 && a4 != a3 && a4 != a2 && a4 != a1); + +	xml_attribute a5 = node.insert_copy_after(a4, a1); +	CHECK(a5 && a5 != a4 && a5 != a3 && a5 != a2 && a5 != a1); + +	CHECK(child.insert_copy_after(a4, a4) == xml_attribute()); + +	CHECK_NODE(doc, STR("<node a1=\"v1\" a2=\"v2\" a2=\"v2\" a1=\"v1\"><child a2=\"v2\" /></node>")); + +	a3.set_name(STR("a3")); +	a3 = STR("v3"); +	 +	a4.set_name(STR("a4")); +	a4 = STR("v4"); +	 +	a5.set_name(STR("a5")); +	a5 = STR("v5"); +	 +	CHECK_NODE(doc, STR("<node a1=\"v1\" a5=\"v5\" a4=\"v4\" a3=\"v3\"><child a2=\"v2\" /></node>")); +} + +TEST_XML(dom_node_insert_copy_before_attribute, "<node a1='v1'><child a2='v2'/></node>") +{ +	CHECK(xml_node().insert_copy_before(xml_attribute(), xml_attribute()) == xml_attribute()); + +	xml_node node = doc.child(STR("node")); +	xml_node child = node.child(STR("child")); + +	xml_attribute a1 = node.attribute(STR("a1")); +	xml_attribute a2 = child.attribute(STR("a2")); + +	CHECK(node.insert_copy_before(a1, xml_attribute()) == xml_attribute()); +	CHECK(node.insert_copy_before(xml_attribute(), a1) == xml_attribute()); +	CHECK(node.insert_copy_before(a2, a2) == xml_attribute()); +	 +	xml_attribute a3 = node.insert_copy_before(a1, a1); +	CHECK(a3 && a3 != a2 && a3 != a1); + +	xml_attribute a4 = node.insert_copy_before(a2, a1); +	CHECK(a4 && a4 != a3 && a4 != a2 && a4 != a1); + +	xml_attribute a5 = node.insert_copy_before(a4, a1); +	CHECK(a5 && a5 != a4 && a5 != a3 && a5 != a2 && a5 != a1); + +	CHECK(child.insert_copy_before(a4, a4) == xml_attribute()); + +	CHECK_NODE(doc, STR("<node a1=\"v1\" a2=\"v2\" a2=\"v2\" a1=\"v1\"><child a2=\"v2\" /></node>")); + +	a3.set_name(STR("a3")); +	a3 = STR("v3"); +	 +	a4.set_name(STR("a4")); +	a4 = STR("v4"); +	 +	a5.set_name(STR("a5")); +	a5 = STR("v5"); +	 +	CHECK_NODE(doc, STR("<node a3=\"v3\" a4=\"v4\" a5=\"v5\" a1=\"v1\"><child a2=\"v2\" /></node>")); +} + +TEST_XML(dom_node_remove_attribute, "<node a1='v1' a2='v2' a3='v3'><child a4='v4'/></node>") +{ +	CHECK(!xml_node().remove_attribute(STR("a"))); +	CHECK(!xml_node().remove_attribute(xml_attribute())); +	 +	xml_node node = doc.child(STR("node")); +	xml_node child = node.child(STR("child")); + +	CHECK(!node.remove_attribute(STR("a"))); +	CHECK(!node.remove_attribute(xml_attribute())); +	CHECK(!node.remove_attribute(child.attribute(STR("a4")))); + +	CHECK_NODE(doc, STR("<node a1=\"v1\" a2=\"v2\" a3=\"v3\"><child a4=\"v4\" /></node>")); + +	CHECK(node.remove_attribute(STR("a1"))); +	CHECK(node.remove_attribute(node.attribute(STR("a3")))); +	CHECK(child.remove_attribute(STR("a4"))); + +	CHECK_NODE(doc, STR("<node a2=\"v2\"><child /></node>")); +} + +TEST_XML(dom_node_append_child, "<node>foo<child/></node>") +{ +	CHECK(xml_node().append_child() == xml_node()); +	CHECK(doc.child(STR("node")).first_child().append_child() == xml_node()); +	CHECK(doc.append_child(node_document) == xml_node()); +	CHECK(doc.append_child(node_null) == xml_node()); +	 +	xml_node n1 = doc.child(STR("node")).append_child(); +	CHECK(n1); +	CHECK(n1.set_name(STR("n1"))); + +	xml_node n2 = doc.child(STR("node")).append_child(); +	CHECK(n2 && n1 != n2); +	CHECK(n2.set_name(STR("n2"))); + +	xml_node n3 = doc.child(STR("node")).child(STR("child")).append_child(node_pcdata); +	CHECK(n3 && n1 != n3 && n2 != n3); +	CHECK(n3.set_value(STR("n3"))); +	 +	xml_node n4 = doc.append_child(node_comment); +	CHECK(n4 && n1 != n4 && n2 != n4 && n3 != n4); +	CHECK(n4.set_value(STR("n4"))); + +	CHECK_NODE(doc, STR("<node>foo<child>n3</child><n1 /><n2 /></node><!--n4-->")); +} + +TEST_XML(dom_node_insert_child_after, "<node>foo<child/></node>") +{ +	CHECK(xml_node().insert_child_after(node_element, xml_node()) == xml_node()); +	CHECK(doc.child(STR("node")).first_child().insert_child_after(node_element, xml_node()) == xml_node()); +	CHECK(doc.insert_child_after(node_document, xml_node()) == xml_node()); +	CHECK(doc.insert_child_after(node_null, xml_node()) == xml_node()); + +	xml_node node = doc.child(STR("node")); +	xml_node child = node.child(STR("child")); + +	CHECK(node.insert_child_after(node_element, node) == xml_node()); +	CHECK(child.insert_child_after(node_element, node) == xml_node()); +	 +	xml_node n1 = node.insert_child_after(node_element, child); +	CHECK(n1 && n1 != node && n1 != child); +	CHECK(n1.set_name(STR("n1"))); + +	xml_node n2 = node.insert_child_after(node_element, child); +	CHECK(n2 && n2 != node && n2 != child && n2 != n1); +	CHECK(n2.set_name(STR("n2"))); + +	xml_node n3 = node.insert_child_after(node_pcdata, n2); +	CHECK(n3 && n3 != node && n3 != child && n3 != n1 && n3 != n2); +	CHECK(n3.set_value(STR("n3"))); + +	xml_node n4 = node.insert_child_after(node_pi, node.first_child()); +	CHECK(n4 && n4 != node && n4 != child && n4 != n1 && n4 != n2 && n4 != n3); +	CHECK(n4.set_name(STR("n4"))); + +	CHECK(child.insert_child_after(node_element, n3) == xml_node()); + +	CHECK_NODE(doc, STR("<node>foo<?n4?><child /><n2 />n3<n1 /></node>")); +} + +TEST_XML(dom_node_insert_child_before, "<node>foo<child/></node>") +{ +	CHECK(xml_node().insert_child_before(node_element, xml_node()) == xml_node()); +	CHECK(doc.child(STR("node")).first_child().insert_child_before(node_element, xml_node()) == xml_node()); +	CHECK(doc.insert_child_before(node_document, xml_node()) == xml_node()); +	CHECK(doc.insert_child_before(node_null, xml_node()) == xml_node()); + +	xml_node node = doc.child(STR("node")); +	xml_node child = node.child(STR("child")); + +	CHECK(node.insert_child_before(node_element, node) == xml_node()); +	CHECK(child.insert_child_before(node_element, node) == xml_node()); +	 +	xml_node n1 = node.insert_child_before(node_element, child); +	CHECK(n1 && n1 != node && n1 != child); +	CHECK(n1.set_name(STR("n1"))); + +	xml_node n2 = node.insert_child_before(node_element, child); +	CHECK(n2 && n2 != node && n2 != child && n2 != n1); +	CHECK(n2.set_name(STR("n2"))); + +	xml_node n3 = node.insert_child_before(node_pcdata, n2); +	CHECK(n3 && n3 != node && n3 != child && n3 != n1 && n3 != n2); +	CHECK(n3.set_value(STR("n3"))); + +	xml_node n4 = node.insert_child_before(node_pi, node.first_child()); +	CHECK(n4 && n4 != node && n4 != child && n4 != n1 && n4 != n2 && n4 != n3); +	CHECK(n4.set_name(STR("n4"))); + +	CHECK(child.insert_child_before(node_element, n3) == xml_node()); + +	CHECK_NODE(doc, STR("<node><?n4?>foo<n1 />n3<n2 /><child /></node>")); +} + +TEST_XML(dom_node_remove_child, "<node><n1/><n2/><n3/><child><n4/></child></node>") +{ +	CHECK(!xml_node().remove_child(STR("a"))); +	CHECK(!xml_node().remove_child(xml_node())); +	 +	xml_node node = doc.child(STR("node")); +	xml_node child = node.child(STR("child")); + +	CHECK(!node.remove_child(STR("a"))); +	CHECK(!node.remove_child(xml_node())); +	CHECK(!node.remove_child(child.child(STR("n4")))); + +	CHECK_NODE(doc, STR("<node><n1 /><n2 /><n3 /><child><n4 /></child></node>")); + +	CHECK(node.remove_child(STR("n1"))); +	CHECK(node.remove_child(node.child(STR("n3")))); +	CHECK(child.remove_child(STR("n4"))); + +	CHECK_NODE(doc, STR("<node><n2 /><child /></node>")); +} + +TEST_XML(dom_node_remove_child_complex, "<node id='1'><n1 id1='1' id2='2'/><n2/><n3/><child><n4/></child></node>") +{ +	doc.child(STR("node")).remove_child(STR("n1")); + +	CHECK_NODE(doc, STR("<node id=\"1\"><n2 /><n3 /><child><n4 /></child></node>")); + +	CHECK(doc.remove_child(STR("node"))); + +	CHECK_NODE(doc, STR("")); +} + +TEST_XML(dom_node_remove_child_complex_allocated, "<node id='1'><n1 id1='1' id2='2'/><n2/><n3/><child><n4/></child></node>") +{ +	doc.append_copy(doc.child(STR("node"))); + +	CHECK(doc.remove_child(STR("node"))); +	CHECK(doc.remove_child(STR("node"))); + +	CHECK_NODE(doc, STR("")); +} + +TEST_XML(dom_node_append_copy, "<node>foo<child/></node>") +{ +	CHECK(xml_node().append_copy(xml_node()) == xml_node()); +	CHECK(doc.child(STR("node")).first_child().append_copy(doc.child(STR("node"))) == xml_node()); +	CHECK(doc.append_copy(doc) == xml_node()); +	CHECK(doc.append_copy(xml_node()) == xml_node()); +	 +	xml_node n1 = doc.child(STR("node")).append_copy(doc.child(STR("node")).first_child()); +	CHECK(n1); +	CHECK_STRING(n1.value(), STR("foo")); +	CHECK_NODE(doc, STR("<node>foo<child />foo</node>")); + +	xml_node n2 = doc.child(STR("node")).append_copy(doc.child(STR("node")).child(STR("child"))); +	CHECK(n2 && n2 != n1); +	CHECK_STRING(n2.name(), STR("child")); +	CHECK_NODE(doc, STR("<node>foo<child />foo<child /></node>")); + +	xml_node n3 = doc.child(STR("node")).child(STR("child")).append_copy(doc.child(STR("node")).first_child()); +	CHECK(n3 && n3 != n1 && n3 != n2); +	CHECK_STRING(n3.value(), STR("foo")); +	CHECK_NODE(doc, STR("<node>foo<child>foo</child>foo<child /></node>")); +} + +TEST_XML(dom_node_insert_copy_after, "<node>foo<child/></node>") +{ +	CHECK(xml_node().insert_copy_after(xml_node(), xml_node()) == xml_node()); +	CHECK(doc.child(STR("node")).first_child().insert_copy_after(doc.child(STR("node")), doc.child(STR("node"))) == xml_node()); +	CHECK(doc.insert_copy_after(doc, doc) == xml_node()); +	CHECK(doc.insert_copy_after(xml_node(), doc.child(STR("node"))) == xml_node()); +	CHECK(doc.insert_copy_after(doc.child(STR("node")), xml_node()) == xml_node()); +	 +	xml_node n1 = doc.child(STR("node")).insert_copy_after(doc.child(STR("node")).child(STR("child")), doc.child(STR("node")).first_child()); +	CHECK(n1); +	CHECK_STRING(n1.name(), STR("child")); +	CHECK_NODE(doc, STR("<node>foo<child /><child /></node>")); + +	xml_node n2 = doc.child(STR("node")).insert_copy_after(doc.child(STR("node")).first_child(), doc.child(STR("node")).last_child()); +	CHECK(n2 && n2 != n1); +	CHECK_STRING(n2.value(), STR("foo")); +	CHECK_NODE(doc, STR("<node>foo<child /><child />foo</node>")); + +	xml_node n3 = doc.child(STR("node")).insert_copy_after(doc.child(STR("node")).first_child(), doc.child(STR("node")).first_child()); +	CHECK(n3 && n3 != n1 && n3 != n2); +	CHECK_STRING(n3.value(), STR("foo")); +	CHECK_NODE(doc, STR("<node>foofoo<child /><child />foo</node>")); +} + +TEST_XML(dom_node_insert_copy_before, "<node>foo<child/></node>") +{ +	CHECK(xml_node().insert_copy_before(xml_node(), xml_node()) == xml_node()); +	CHECK(doc.child(STR("node")).first_child().insert_copy_before(doc.child(STR("node")), doc.child(STR("node"))) == xml_node()); +	CHECK(doc.insert_copy_before(doc, doc) == xml_node()); +	CHECK(doc.insert_copy_before(xml_node(), doc.child(STR("node"))) == xml_node()); +	CHECK(doc.insert_copy_before(doc.child(STR("node")), xml_node()) == xml_node()); +	 +	xml_node n1 = doc.child(STR("node")).insert_copy_before(doc.child(STR("node")).child(STR("child")), doc.child(STR("node")).first_child()); +	CHECK(n1); +	CHECK_STRING(n1.name(), STR("child")); +	CHECK_NODE(doc, STR("<node><child />foo<child /></node>")); + +	xml_node n2 = doc.child(STR("node")).insert_copy_before(doc.child(STR("node")).first_child(), doc.child(STR("node")).last_child()); +	CHECK(n2 && n2 != n1); +	CHECK_STRING(n2.name(), STR("child")); +	CHECK_NODE(doc, STR("<node><child />foo<child /><child /></node>")); + +	xml_node n3 = doc.child(STR("node")).insert_copy_before(doc.child(STR("node")).first_child().next_sibling(), doc.child(STR("node")).first_child()); +	CHECK(n3 && n3 != n1 && n3 != n2); +	CHECK_STRING(n3.value(), STR("foo")); +	CHECK_NODE(doc, STR("<node>foo<child />foo<child /><child /></node>")); +} + +TEST_XML(dom_node_copy_recursive, "<node>foo<child/></node>") +{ +	doc.child(STR("node")).append_copy(doc.child(STR("node"))); +	CHECK_NODE(doc, STR("<node>foo<child /><node>foo<child /></node></node>")); +} + +TEST_XML(dom_node_copy_crossdoc, "<node/>") +{ +	xml_document newdoc; +	newdoc.append_copy(doc.child(STR("node"))); +	CHECK_NODE(doc, STR("<node />")); +	CHECK_NODE(newdoc, STR("<node />")); +} + +TEST_XML_FLAGS(dom_node_copy_types, "<?xml version='1.0'?><root><?pi value?><!--comment--><node id='1'>pcdata<![CDATA[cdata]]></node></root>", parse_default | parse_pi | parse_comments | parse_declaration) +{ +	doc.append_copy(doc.child(STR("root"))); +	CHECK_NODE(doc, STR("<?xml version=\"1.0\"?><root><?pi value?><!--comment--><node id=\"1\">pcdata<![CDATA[cdata]]></node></root><root><?pi value?><!--comment--><node id=\"1\">pcdata<![CDATA[cdata]]></node></root>")); + +	doc.insert_copy_before(doc.first_child(), doc.first_child()); +	CHECK_NODE(doc, STR("<?xml version=\"1.0\"?><?xml version=\"1.0\"?><root><?pi value?><!--comment--><node id=\"1\">pcdata<![CDATA[cdata]]></node></root><root><?pi value?><!--comment--><node id=\"1\">pcdata<![CDATA[cdata]]></node></root>")); +} + +TEST_XML(dom_attr_assign_large_number, "<node attr1='' attr2='' />") +{ +	xml_node node = doc.child(STR("node")); + +	node.attribute(STR("attr1")) = FLT_MAX; +	node.attribute(STR("attr2")) = DBL_MAX; + +	CHECK(test_node(node, STR("<node attr1=\"3.40282e+038\" attr2=\"1.79769e+308\" />"), STR(""), pugi::format_raw) || +		  test_node(node, STR("<node attr1=\"3.40282e+38\" attr2=\"1.79769e+308\" />"), STR(""), pugi::format_raw)); +} + +TEST(dom_node_declaration_name) +{ +	xml_document doc; +	doc.append_child(node_declaration); + +	// name 'xml' is auto-assigned +	CHECK(doc.first_child().type() == node_declaration); +	CHECK_STRING(doc.first_child().name(), STR("xml")); + +	doc.insert_child_after(node_declaration, doc.first_child()); +	doc.insert_child_before(node_declaration, doc.first_child()); + +	CHECK_NODE(doc, STR("<?xml?><?xml?><?xml?>")); +} + +TEST(dom_node_declaration_top_level) +{ +	xml_document doc; +	doc.append_child().set_name(STR("node")); + +	xml_node node = doc.first_child(); +	node.append_child(node_pcdata).set_value(STR("text")); + +	CHECK(node.insert_child_before(node_declaration, node.first_child()) == xml_node()); +	CHECK(node.insert_child_after(node_declaration, node.first_child()) == xml_node()); +	CHECK(node.append_child(node_declaration) == xml_node()); + +	CHECK_NODE(doc, STR("<node>text</node>")); + +	CHECK(doc.insert_child_before(node_declaration, node)); +	CHECK(doc.insert_child_after(node_declaration, node)); +	CHECK(doc.append_child(node_declaration)); + +	CHECK_NODE(doc, STR("<?xml?><node>text</node><?xml?><?xml?>")); +} + +TEST(dom_node_declaration_copy) +{ +	xml_document doc; +	doc.append_child(node_declaration); + +	doc.append_child().set_name(STR("node")); + +	doc.last_child().append_copy(doc.first_child()); + +	CHECK_NODE(doc, STR("<?xml?><node />")); +} + +TEST(dom_string_out_of_memory) +{ +	unsigned int length = 65536; + +	char_t* string = new char_t[length + 1]; +	for (unsigned int i = 0; i < length; ++i) string[i] = 'a'; +	string[length] = 0; + +	xml_document doc; +	xml_node node = doc.append_child(); +	xml_attribute attr = node.append_attribute(STR("a")); +	xml_node text = node.append_child(node_pcdata); + +	// no value => long value +	test_runner::_memory_fail_threshold = 32; + +	CHECK(!node.set_name(string)); +	CHECK(!text.set_value(string)); +	CHECK(!attr.set_name(string)); +	CHECK(!attr.set_value(string)); + +	// set some names/values +	test_runner::_memory_fail_threshold = 0; + +	node.set_name(STR("n")); +	attr.set_value(STR("v")); +	text.set_value(STR("t")); + +	// some value => long value +	test_runner::_memory_fail_threshold = 32; + +	CHECK(!node.set_name(string)); +	CHECK(!text.set_value(string)); +	CHECK(!attr.set_name(string)); +	CHECK(!attr.set_value(string)); + +	// check that original state was preserved +	test_runner::_memory_fail_threshold = 0; + +	CHECK_NODE(doc, STR("<n a=\"v\">t</n>")); +} + +TEST(dom_node_out_of_memory) +{ +	test_runner::_memory_fail_threshold = 65536; + +	// exhaust memory limit +	xml_document doc; + +	xml_node n = doc.append_child(); +	CHECK(n.set_name(STR("n"))); + +	xml_attribute a = n.append_attribute(STR("a")); +	CHECK(a); + +	while (n.append_child(node_comment) || n.append_attribute(STR("b"))) +	{ +		// nop +	} + +	// verify all node modification operations +	CHECK(!n.append_child()); +	CHECK(!n.insert_child_after(node_element, n.first_child())); +	CHECK(!n.insert_child_before(node_element, n.first_child())); +	CHECK(!n.append_attribute(STR(""))); +	CHECK(!n.insert_attribute_after(STR(""), a)); +	CHECK(!n.insert_attribute_before(STR(""), a)); + +	// verify node copy operations +	CHECK(!n.append_copy(n.first_child())); +	CHECK(!n.insert_copy_after(n.first_child(), n.first_child())); +	CHECK(!n.insert_copy_before(n.first_child(), n.first_child())); +	CHECK(!n.append_copy(a)); +	CHECK(!n.insert_copy_after(a, a)); +	CHECK(!n.insert_copy_before(a, a)); +} diff --git a/tests/test_dom_traverse.cpp b/tests/test_dom_traverse.cpp index 896bf6f..8075dc3 100644 --- a/tests/test_dom_traverse.cpp +++ b/tests/test_dom_traverse.cpp @@ -1,756 +1,756 @@ -#define _CRT_SECURE_NO_WARNINGS
 -#define _SCL_SECURE_NO_WARNINGS
 -
 -#include "common.hpp"
 -
 -#include <stdio.h>
 -
 -#include <string.h>
 -#include <wchar.h>
 -
 -#include <utility>
 -#include <vector>
 -#include <iterator>
 -#include <string>
 -
 -#include "helpers.hpp"
 -
 -#ifdef PUGIXML_NO_STL
 -template <typename I> static I move_iter(I base, int n)
 -{
 -	if (n > 0) while (n--) ++base;
 -	else while (n++) --base;
 -	return base;
 -}
 -#else
 -template <typename I> static I move_iter(I base, int n)
 -{
 -	std::advance(base, n);
 -	return base;
 -}
 -#endif
 -
 -template <typename T> static void generic_empty_test(const T& obj)
 -{
 -	T null;
 -
 -	CHECK(null.empty());
 -	CHECK(!obj.empty());
 -}
 -
 -TEST_XML(dom_attr_bool_ops, "<node attr='1'/>")
 -{
 -	generic_bool_ops_test(doc.child(STR("node")).attribute(STR("attr")));
 -}
 -
 -TEST_XML(dom_attr_eq_ops, "<node attr1='1' attr2='2'/>")
 -{
 -	generic_eq_ops_test(doc.child(STR("node")).attribute(STR("attr1")), doc.child(STR("node")).attribute(STR("attr2")));
 -}
 -
 -TEST_XML(dom_attr_rel_ops, "<node attr1='1' attr2='2'/>")
 -{
 -	generic_rel_ops_test(doc.child(STR("node")).attribute(STR("attr1")), doc.child(STR("node")).attribute(STR("attr2")));
 -}
 -
 -TEST_XML(dom_attr_empty, "<node attr='1'/>")
 -{
 -	generic_empty_test(doc.child(STR("node")).attribute(STR("attr")));
 -}
 -
 -TEST_XML(dom_attr_next_previous_attribute, "<node attr1='1' attr2='2' />")
 -{
 -	xml_attribute attr1 = doc.child(STR("node")).attribute(STR("attr1"));
 -	xml_attribute attr2 = doc.child(STR("node")).attribute(STR("attr2"));
 -
 -	CHECK(attr1.next_attribute() == attr2);
 -	CHECK(attr2.next_attribute() == xml_attribute());
 -	
 -	CHECK(attr1.previous_attribute() == xml_attribute());
 -	CHECK(attr2.previous_attribute() == attr1);
 -	
 -	CHECK(xml_attribute().next_attribute() == xml_attribute());
 -	CHECK(xml_attribute().previous_attribute() == xml_attribute());
 -}
 -
 -TEST_XML(dom_attr_name_value, "<node attr='1'/>")
 -{
 -	xml_attribute attr = doc.child(STR("node")).attribute(STR("attr"));
 -
 -	CHECK_NAME_VALUE(attr, STR("attr"), STR("1"));
 -	CHECK_NAME_VALUE(xml_attribute(), STR(""), STR(""));
 -}
 -
 -TEST_XML(dom_attr_as_int, "<node attr1='1' attr2='-1' attr3='-2147483648' attr4='2147483647'/>")
 -{
 -	xml_node node = doc.child(STR("node"));
 -
 -	CHECK(xml_attribute().as_int() == 0);
 -	CHECK(node.attribute(STR("attr1")).as_int() == 1);
 -	CHECK(node.attribute(STR("attr2")).as_int() == -1);
 -	CHECK(node.attribute(STR("attr3")).as_int() == -2147483647 - 1);
 -	CHECK(node.attribute(STR("attr4")).as_int() == 2147483647);
 -}
 -
 -TEST_XML(dom_attr_as_uint, "<node attr1='0' attr2='1' attr3='2147483647' attr4='4294967295'/>")
 -{
 -	xml_node node = doc.child(STR("node"));
 -
 -	CHECK(xml_attribute().as_uint() == 0);
 -	CHECK(node.attribute(STR("attr1")).as_uint() == 0);
 -	CHECK(node.attribute(STR("attr2")).as_uint() == 1);
 -	CHECK(node.attribute(STR("attr3")).as_uint() == 2147483647);
 -	CHECK(node.attribute(STR("attr4")).as_uint() == 4294967295u);
 -}
 -
 -TEST_XML(dom_attr_as_float, "<node attr1='0' attr2='1' attr3='0.12' attr4='-5.1' attr5='3e-4' attr6='3.14159265358979323846'/>")
 -{
 -	xml_node node = doc.child(STR("node"));
 -
 -	CHECK(xml_attribute().as_float() == 0);
 -	CHECK_DOUBLE(node.attribute(STR("attr1")).as_float(), 0);
 -	CHECK_DOUBLE(node.attribute(STR("attr2")).as_float(), 1);
 -	CHECK_DOUBLE(node.attribute(STR("attr3")).as_float(), 0.12);
 -	CHECK_DOUBLE(node.attribute(STR("attr4")).as_float(), -5.1);
 -	CHECK_DOUBLE(node.attribute(STR("attr5")).as_float(), 3e-4);
 -	CHECK_DOUBLE(node.attribute(STR("attr6")).as_float(), 3.14159265358979323846);
 -}
 -
 -TEST_XML(dom_attr_as_double, "<node attr1='0' attr2='1' attr3='0.12' attr4='-5.1' attr5='3e-4' attr6='3.14159265358979323846'/>")
 -{
 -	xml_node node = doc.child(STR("node"));
 -
 -	CHECK(xml_attribute().as_double() == 0);
 -	CHECK_DOUBLE(node.attribute(STR("attr1")).as_double(), 0);
 -	CHECK_DOUBLE(node.attribute(STR("attr2")).as_double(), 1);
 -	CHECK_DOUBLE(node.attribute(STR("attr3")).as_double(), 0.12);
 -	CHECK_DOUBLE(node.attribute(STR("attr4")).as_double(), -5.1);
 -	CHECK_DOUBLE(node.attribute(STR("attr5")).as_double(), 3e-4);
 -	CHECK_DOUBLE(node.attribute(STR("attr6")).as_double(), 3.14159265358979323846);
 -}
 -
 -TEST_XML(dom_attr_as_bool, "<node attr1='0' attr2='1' attr3='true' attr4='True' attr5='Yes' attr6='yes' attr7='false'/>")
 -{
 -	xml_node node = doc.child(STR("node"));
 -
 -	CHECK(!xml_attribute().as_bool());
 -	CHECK(!node.attribute(STR("attr1")).as_bool());
 -	CHECK(node.attribute(STR("attr2")).as_bool());
 -	CHECK(node.attribute(STR("attr3")).as_bool());
 -	CHECK(node.attribute(STR("attr4")).as_bool());
 -	CHECK(node.attribute(STR("attr5")).as_bool());
 -	CHECK(node.attribute(STR("attr6")).as_bool());
 -	CHECK(!node.attribute(STR("attr7")).as_bool());
 -}
 -
 -TEST_XML(dom_attr_iterator, "<node><node1 attr1='0'/><node2 attr1='0' attr2='1'/><node3/></node>")
 -{
 -	xml_node node1 = doc.child(STR("node")).child(STR("node1"));
 -	xml_node node2 = doc.child(STR("node")).child(STR("node2"));
 -	xml_node node3 = doc.child(STR("node")).child(STR("node3"));
 -
 -	CHECK(xml_node().attributes_begin() == xml_attribute_iterator());
 -	CHECK(xml_node().attributes_end() == xml_attribute_iterator());
 -
 -	CHECK(node1.attributes_begin() == xml_attribute_iterator(node1.attribute(STR("attr1")), node1));
 -	CHECK(move_iter(node1.attributes_begin(), 1) == node1.attributes_end());
 -	CHECK(move_iter(node1.attributes_end(), -1) == node1.attributes_begin());
 -	CHECK(*node1.attributes_begin() == node1.attribute(STR("attr1")));
 -	CHECK_STRING(node1.attributes_begin()->name(), STR("attr1"));
 -
 -	CHECK(move_iter(node2.attributes_begin(), 2) == node2.attributes_end());
 -	CHECK(move_iter(node2.attributes_end(), -2) == node2.attributes_begin());
 -
 -	CHECK(node3.attributes_begin() != xml_attribute_iterator());
 -	CHECK(node3.attributes_begin() == node3.attributes_end());
 -
 -	xml_attribute_iterator it = xml_attribute_iterator(node2.attribute(STR("attr2")), node2);
 -	xml_attribute_iterator itt = it;
 -
 -	CHECK(itt++ == it);
 -	CHECK(itt == node2.attributes_end());
 -
 -	CHECK(itt-- == node2.attributes_end());
 -	CHECK(itt == it);
 -
 -	CHECK(++itt == node2.attributes_end());
 -	CHECK(itt == node2.attributes_end());
 -
 -	CHECK(--itt == it);
 -	CHECK(itt == it);
 -
 -	CHECK(++itt != it);
 -}
 -
 -TEST_XML(dom_attr_iterator_end, "<node><node1 attr1='0'/><node2 attr1='0' attr2='1'/><node3/></node>")
 -{
 -	xml_node node1 = doc.child(STR("node")).child(STR("node1"));
 -	xml_node node2 = doc.child(STR("node")).child(STR("node2"));
 -	xml_node node3 = doc.child(STR("node")).child(STR("node3"));
 -
 -	CHECK(node1.attributes_end() != node2.attributes_end() && node1.attributes_end() != node3.attributes_end() && node2.attributes_end() != node3.attributes_end());
 -	CHECK(node1.attributes_end() != xml_attribute_iterator() && node2.attributes_end() != xml_attribute_iterator() && node3.attributes_end() != xml_attribute_iterator());
 -}
 -
 -TEST_XML(dom_attr_iterator_invalidate, "<node><node1 attr1='0'/><node2 attr1='0' attr2='1'/><node3/></node>")
 -{
 -	xml_node node2 = doc.child(STR("node")).child(STR("node2"));
 -
 -	xml_attribute_iterator it1 = node2.attributes_begin();
 -	xml_attribute_iterator it2 = move_iter(it1, 1);
 -	xml_attribute_iterator it3 = move_iter(it2, 1);
 -
 -	CHECK(it3 == node2.attributes_end());
 -
 -	// removing attr2, it2 is invalid now, it3 is still past-the-end
 -	node2.remove_attribute(*it2);
 -
 -	CHECK(node2.attributes_end() == it3);
 -	CHECK(move_iter(it1, 1) == it3);
 -	CHECK(move_iter(it3, -1) == it1);
 -	CHECK_STRING(it1->name(), STR("attr1"));
 -
 -	// adding attr2 back, it3 is still past-the-end!
 -	xml_attribute_iterator it2new = xml_attribute_iterator(node2.append_attribute(STR("attr2-new")), node2);
 -
 -	CHECK(node2.attributes_end() == it3);
 -	CHECK(move_iter(it1, 1) == it2new);
 -	CHECK(move_iter(it2new, 1) == it3);
 -	CHECK(move_iter(it3, -1) == it2new);
 -	CHECK_STRING(it2new->name(), STR("attr2-new"));
 -
 -	// removing both attributes, it3 is now equal to the begin
 -	node2.remove_attribute(*it1);
 -	node2.remove_attribute(*it2new);
 -	CHECK(!node2.first_attribute());
 -
 -	CHECK(node2.attributes_begin() == it3);
 -	CHECK(node2.attributes_end() == it3);
 -}
 -
 -TEST_XML(dom_node_bool_ops, "<node/>")
 -{
 -	generic_bool_ops_test(doc.child(STR("node")));
 -}
 -
 -TEST_XML(dom_node_eq_ops, "<node><node1/><node2/></node>")
 -{
 -	generic_eq_ops_test(doc.child(STR("node")).child(STR("node1")), doc.child(STR("node")).child(STR("node2")));
 -}
 -
 -TEST_XML(dom_node_rel_ops, "<node><node1/><node2/></node>")
 -{
 -	generic_rel_ops_test(doc.child(STR("node")).child(STR("node1")), doc.child(STR("node")).child(STR("node2")));
 -}
 -
 -TEST_XML(dom_node_empty, "<node/>")
 -{
 -	generic_empty_test(doc.child(STR("node")));
 -}
 -
 -TEST_XML(dom_node_iterator, "<node><node1><child1/></node1><node2><child1/><child2/></node2><node3/></node>")
 -{
 -	xml_node node1 = doc.child(STR("node")).child(STR("node1"));
 -	xml_node node2 = doc.child(STR("node")).child(STR("node2"));
 -	xml_node node3 = doc.child(STR("node")).child(STR("node3"));
 -
 -	CHECK(xml_node().begin() == xml_node_iterator());
 -	CHECK(xml_node().end() == xml_node_iterator());
 -
 -	CHECK(node1.begin() == xml_node_iterator(node1.child(STR("child1"))));
 -	CHECK(move_iter(node1.begin(), 1) == node1.end());
 -	CHECK(move_iter(node1.end(), -1) == node1.begin());
 -	CHECK(*node1.begin() == node1.child(STR("child1")));
 -	CHECK_STRING(node1.begin()->name(), STR("child1"));
 -
 -	CHECK(move_iter(node2.begin(), 2) == node2.end());
 -	CHECK(move_iter(node2.end(), -2) == node2.begin());
 -
 -	CHECK(node3.begin() != xml_node_iterator());
 -	CHECK(node3.begin() == node3.end());
 -
 -	xml_node_iterator it = node2.child(STR("child2"));
 -	xml_node_iterator itt = it;
 -
 -	CHECK(itt++ == it);
 -	CHECK(itt == node2.end());
 -
 -	CHECK(itt-- == node2.end());
 -	CHECK(itt == it);
 -
 -	CHECK(++itt == node2.end());
 -	CHECK(itt == node2.end());
 -
 -	CHECK(--itt == it);
 -	CHECK(itt == it);
 -
 -	CHECK(++itt != it);
 -}
 -
 -TEST_XML(dom_node_iterator_end, "<node><node1><child1/></node1><node2><child1/><child2/></node2><node3/></node>")
 -{
 -	xml_node node1 = doc.child(STR("node")).child(STR("node1"));
 -	xml_node node2 = doc.child(STR("node")).child(STR("node2"));
 -	xml_node node3 = doc.child(STR("node")).child(STR("node3"));
 -
 -	CHECK(node1.end() != node2.end() && node1.end() != node3.end() && node2.end() != node3.end());
 -	CHECK(node1.end() != xml_node_iterator() && node2.end() != xml_node_iterator() && node3.end() != xml_node_iterator());
 -}
 -
 -TEST_XML(dom_node_iterator_invalidate, "<node><node1><child1/></node1><node2><child1/><child2/></node2><node3/></node>")
 -{
 -	xml_node node2 = doc.child(STR("node")).child(STR("node2"));
 -
 -	xml_node_iterator it1 = node2.begin();
 -	xml_node_iterator it2 = move_iter(it1, 1);
 -	xml_node_iterator it3 = move_iter(it2, 1);
 -
 -	CHECK(it3 == node2.end());
 -
 -	// removing child2, it2 is invalid now, it3 is still past-the-end
 -	node2.remove_child(*it2);
 -
 -	CHECK(node2.end() == it3);
 -	CHECK(move_iter(it1, 1) == it3);
 -	CHECK(move_iter(it3, -1) == it1);
 -	CHECK_STRING(it1->name(), STR("child1"));
 -
 -	// adding attr2 back, it3 is still past-the-end!
 -	xml_node_iterator it2new = node2.append_child();
 -	it2new->set_name(STR("child2-new"));
 -
 -	CHECK(node2.end() == it3);
 -	CHECK(move_iter(it1, 1) == it2new);
 -	CHECK(move_iter(it2new, 1) == it3);
 -	CHECK(move_iter(it3, -1) == it2new);
 -	CHECK_STRING(it2new->name(), STR("child2-new"));
 -
 -	// removing both nodes, it3 is now equal to the begin
 -	node2.remove_child(*it1);
 -	node2.remove_child(*it2new);
 -	CHECK(!node2.first_child());
 -
 -	CHECK(node2.begin() == it3);
 -	CHECK(node2.end() == it3);
 -}
 -
 -TEST_XML(dom_node_parent, "<node><child/></node>")
 -{
 -	CHECK(xml_node().parent() == xml_node());
 -	CHECK(doc.child(STR("node")).child(STR("child")).parent() == doc.child(STR("node")));
 -	CHECK(doc.child(STR("node")).parent() == doc);
 -}
 -
 -TEST_XML(dom_node_root, "<node><child/></node>")
 -{
 -	CHECK(xml_node().root() == xml_node());
 -	CHECK(doc.child(STR("node")).child(STR("child")).root() == doc);
 -	CHECK(doc.child(STR("node")).root() == doc);
 -}
 -
 -TEST_XML_FLAGS(dom_node_type, "<?xml?><?pi?><!--comment--><node>pcdata<![CDATA[cdata]]></node>", parse_default | parse_pi | parse_comments | parse_declaration)
 -{
 -	CHECK(xml_node().type() == node_null);
 -	CHECK(doc.type() == node_document);
 -
 -	xml_node_iterator it = doc.begin();
 -
 -	CHECK((it++)->type() == node_declaration);
 -	CHECK((it++)->type() == node_pi);
 -	CHECK((it++)->type() == node_comment);
 -	CHECK((it++)->type() == node_element);
 -
 -	xml_node_iterator cit = doc.child(STR("node")).begin();
 -	
 -	CHECK((cit++)->type() == node_pcdata);
 -	CHECK((cit++)->type() == node_cdata);
 -}
 -
 -TEST_XML_FLAGS(dom_node_name_value, "<?xml?><?pi?><!--comment--><node>pcdata<![CDATA[cdata]]></node>", parse_default | parse_pi | parse_comments | parse_declaration)
 -{
 -	CHECK_NAME_VALUE(xml_node(), STR(""), STR(""));
 -	CHECK_NAME_VALUE(doc, STR(""), STR(""));
 -
 -	xml_node_iterator it = doc.begin();
 -
 -	CHECK_NAME_VALUE(*it++, STR("xml"), STR(""));
 -	CHECK_NAME_VALUE(*it++, STR("pi"), STR(""));
 -	CHECK_NAME_VALUE(*it++, STR(""), STR("comment"));
 -	CHECK_NAME_VALUE(*it++, STR("node"), STR(""));
 -
 -	xml_node_iterator cit = doc.child(STR("node")).begin();
 -	
 -	CHECK_NAME_VALUE(*cit++, STR(""), STR("pcdata"));
 -	CHECK_NAME_VALUE(*cit++, STR(""), STR("cdata"));
 -}
 -
 -TEST_XML(dom_node_child, "<node><child1/><child2/></node>")
 -{
 -	CHECK(xml_node().child(STR("n")) == xml_node());
 -
 -	CHECK(doc.child(STR("n")) == xml_node());
 -	CHECK_NAME_VALUE(doc.child(STR("node")), STR("node"), STR(""));
 -	CHECK(doc.child(STR("node")).child(STR("child2")) == doc.child(STR("node")).last_child());
 -}
 -
 -TEST_XML(dom_node_attribute, "<node attr1='0' attr2='1'/>")
 -{
 -	CHECK(xml_node().attribute(STR("a")) == xml_attribute());
 -
 -	xml_node node = doc.child(STR("node"));
 -
 -	CHECK(node.attribute(STR("n")) == xml_attribute());
 -	CHECK_NAME_VALUE(node.attribute(STR("attr1")), STR("attr1"), STR("0"));
 -	CHECK(node.attribute(STR("attr2")) == node.last_attribute());
 -}
 -
 -TEST_XML(dom_node_next_previous_sibling, "<node><child1/><child2/><child3/></node>")
 -{
 -	CHECK(xml_node().next_sibling() == xml_node());
 -	CHECK(xml_node().next_sibling(STR("n")) == xml_node());
 -
 -	CHECK(xml_node().previous_sibling() == xml_node());
 -	CHECK(xml_node().previous_sibling(STR("n")) == xml_node());
 -
 -	xml_node child1 = doc.child(STR("node")).child(STR("child1"));
 -	xml_node child2 = doc.child(STR("node")).child(STR("child2"));
 -	xml_node child3 = doc.child(STR("node")).child(STR("child3"));
 -
 -	CHECK(child1.next_sibling() == child2);
 -	CHECK(child3.next_sibling() == xml_node());
 -	
 -	CHECK(child1.previous_sibling() == xml_node());
 -	CHECK(child3.previous_sibling() == child2);
 -	
 -	CHECK(child1.next_sibling(STR("child3")) == child3);
 -	CHECK(child1.next_sibling(STR("child")) == xml_node());
 -
 -	CHECK(child3.previous_sibling(STR("child1")) == child1);
 -	CHECK(child3.previous_sibling(STR("child")) == xml_node());
 -}
 -
 -TEST_XML(dom_node_child_value, "<node><novalue/><child1>value1</child1><child2>value2<n/></child2><child3><![CDATA[value3]]></child3>value4</node>")
 -{
 -	CHECK_STRING(xml_node().child_value(), STR(""));
 -	CHECK_STRING(xml_node().child_value(STR("n")), STR(""));
 -
 -	xml_node node = doc.child(STR("node"));
 -
 -	CHECK_STRING(node.child_value(), STR("value4"));
 -	CHECK_STRING(node.child(STR("child1")).child_value(), STR("value1"));
 -	CHECK_STRING(node.child(STR("child2")).child_value(), STR("value2"));
 -	CHECK_STRING(node.child(STR("child3")).child_value(), STR("value3"));
 -	CHECK_STRING(node.child_value(STR("child3")), STR("value3"));
 -}
 -
 -TEST_XML(dom_node_first_last_attribute, "<node attr1='0' attr2='1'/>")
 -{
 -	xml_node node = doc.child(STR("node"));
 -
 -	CHECK(node.first_attribute() == node.attribute(STR("attr1")));
 -	CHECK(node.last_attribute() == node.attribute(STR("attr2")));
 -
 -	CHECK(xml_node().first_attribute() == xml_attribute());
 -	CHECK(xml_node().last_attribute() == xml_attribute());
 -
 -	CHECK(doc.first_attribute() == xml_attribute());
 -	CHECK(doc.last_attribute() == xml_attribute());
 -}
 -
 -TEST_XML(dom_node_first_last_child, "<node><child1/><child2/></node>")
 -{
 -	xml_node node = doc.child(STR("node"));
 -
 -	CHECK(node.first_child() == node.child(STR("child1")));
 -	CHECK(node.last_child() == node.child(STR("child2")));
 -
 -	CHECK(xml_node().first_child() == xml_node());
 -	CHECK(xml_node().last_child() == xml_node());
 -
 -	CHECK(doc.first_child() == node);
 -	CHECK(doc.last_child() == node);
 -}
 -
 -TEST_XML(dom_node_find_child_by_attribute, "<node><child1 attr='value1'/><child2 attr='value2'/><child2 attr='value3'/></node>")
 -{
 -	CHECK(xml_node().find_child_by_attribute(STR("name"), STR("attr"), STR("value")) == xml_node());
 -	CHECK(xml_node().find_child_by_attribute(STR("attr"), STR("value")) == xml_node());
 -
 -	xml_node node = doc.child(STR("node"));
 -
 -	CHECK(node.find_child_by_attribute(STR("child2"), STR("attr"), STR("value3")) == node.last_child());
 -	CHECK(node.find_child_by_attribute(STR("child2"), STR("attr3"), STR("value3")) == xml_node());
 -	CHECK(node.find_child_by_attribute(STR("attr"), STR("value2")) == node.child(STR("child2")));
 -	CHECK(node.find_child_by_attribute(STR("attr3"), STR("value")) == xml_node());
 -}
 -
 -struct find_predicate_const
 -{
 -	bool result;
 -
 -	find_predicate_const(bool result): result(result)
 -	{
 -	}
 -
 -	template <typename T> bool operator()(const T&) const
 -	{
 -		return result;
 -	}
 -};
 -
 -struct find_predicate_prefix
 -{
 -	const pugi::char_t* prefix;
 -
 -	find_predicate_prefix(const pugi::char_t* prefix): prefix(prefix)
 -	{
 -	}
 -
 -	template <typename T> bool operator()(const T& obj) const
 -	{
 -	#ifdef PUGIXML_WCHAR_MODE
 -		// can't use wcsncmp here because of a bug in DMC
 -		return std::basic_string<pugi::char_t>(obj.name()).compare(0, wcslen(prefix), prefix) == 0;
 -	#else
 -		return strncmp(obj.name(), prefix, strlen(prefix)) == 0;
 -	#endif
 -	}
 -};
 -
 -TEST_XML(dom_node_find_attribute, "<node attr1='0' attr2='1'/>")
 -{
 -	CHECK(xml_node().find_attribute(find_predicate_const(true)) == xml_attribute());
 -
 -	xml_node node = doc.child(STR("node"));
 -
 -	CHECK(doc.find_attribute(find_predicate_const(true)) == xml_attribute());
 -	CHECK(node.find_attribute(find_predicate_const(true)) == node.first_attribute());
 -	CHECK(node.find_attribute(find_predicate_const(false)) == xml_attribute());
 -	CHECK(node.find_attribute(find_predicate_prefix(STR("attr2"))) == node.last_attribute());
 -	CHECK(node.find_attribute(find_predicate_prefix(STR("attr"))) == node.first_attribute());
 -}
 -
 -TEST_XML(dom_node_find_child, "<node><child1/><child2/></node>")
 -{
 -	CHECK(xml_node().find_child(find_predicate_const(true)) == xml_node());
 -
 -	xml_node node = doc.child(STR("node"));
 -
 -	CHECK(node.child(STR("node")).child(STR("child1")).find_child(find_predicate_const(true)) == xml_node());
 -	CHECK(node.find_child(find_predicate_const(true)) == node.first_child());
 -	CHECK(node.find_child(find_predicate_const(false)) == xml_node());
 -	CHECK(node.find_child(find_predicate_prefix(STR("child2"))) == node.last_child());
 -	CHECK(node.find_child(find_predicate_prefix(STR("child"))) == node.first_child());
 -}
 -
 -TEST_XML(dom_node_find_node, "<node><child1/><child2/></node>")
 -{
 -	CHECK(xml_node().find_node(find_predicate_const(true)) == xml_node());
 -
 -	xml_node node = doc.child(STR("node"));
 -
 -	CHECK(node.child(STR("node")).child(STR("child1")).find_node(find_predicate_const(true)) == xml_node());
 -	CHECK(node.find_node(find_predicate_const(true)) == node.first_child());
 -	CHECK(node.find_node(find_predicate_const(false)) == xml_node());
 -	CHECK(node.find_node(find_predicate_prefix(STR("child2"))) == node.last_child());
 -	CHECK(node.find_node(find_predicate_prefix(STR("child"))) == node.first_child());
 -	CHECK(doc.find_node(find_predicate_prefix(STR("child"))) == node.first_child());
 -	CHECK(doc.find_node(find_predicate_prefix(STR("child2"))) == node.last_child());
 -	CHECK(doc.find_node(find_predicate_prefix(STR("child3"))) == xml_node());
 -}
 -
 -#ifndef PUGIXML_NO_STL
 -TEST_XML(dom_node_path, "<node><child1>text<child2/></child1></node>")
 -{
 -	CHECK(xml_node().path() == STR(""));
 -	
 -	CHECK(doc.path() == STR(""));
 -	CHECK(doc.child(STR("node")).path() == STR("/node"));
 -	CHECK(doc.child(STR("node")).child(STR("child1")).path() == STR("/node/child1"));
 -	CHECK(doc.child(STR("node")).child(STR("child1")).child(STR("child2")).path() == STR("/node/child1/child2"));
 -	CHECK(doc.child(STR("node")).child(STR("child1")).first_child().path() == STR("/node/child1/"));
 -	
 -	CHECK(doc.child(STR("node")).child(STR("child1")).path('\\') == STR("\\node\\child1"));
 -}
 -#endif
 -
 -TEST_XML(dom_node_first_element_by_path, "<node><child1>text<child2/></child1></node>")
 -{
 -	CHECK(xml_node().first_element_by_path(STR("/")) == xml_node());
 -	
 -	CHECK(doc.first_element_by_path(STR("")) == doc);
 -	CHECK(doc.first_element_by_path(STR("/")) == doc);
 -
 -	CHECK(doc.first_element_by_path(STR("/node/")) == doc.child(STR("node")));
 -	CHECK(doc.first_element_by_path(STR("node/")) == doc.child(STR("node")));
 -	CHECK(doc.first_element_by_path(STR("node")) == doc.child(STR("node")));
 -	CHECK(doc.first_element_by_path(STR("/node")) == doc.child(STR("node")));
 -
 -#ifndef PUGIXML_NO_STL
 -	CHECK(doc.first_element_by_path(STR("/node/child1/child2")).path() == STR("/node/child1/child2"));
 -#endif
 -
 -	CHECK(doc.first_element_by_path(STR("/node/child2")) == xml_node());
 -	
 -	CHECK(doc.first_element_by_path(STR("\\node\\child1"), '\\') == doc.child(STR("node")).child(STR("child1")));
 -
 -	CHECK(doc.child(STR("node")).first_element_by_path(STR("..")) == doc);
 -	CHECK(doc.child(STR("node")).first_element_by_path(STR(".")) == doc.child(STR("node")));
 -
 -	CHECK(doc.child(STR("node")).first_element_by_path(STR("../node/./child1/../.")) == doc.child(STR("node")));
 -
 -	CHECK(doc.child(STR("node")).first_element_by_path(STR("child1")) == doc.child(STR("node")).child(STR("child1")));
 -	CHECK(doc.child(STR("node")).first_element_by_path(STR("child1/")) == doc.child(STR("node")).child(STR("child1")));
 -	CHECK(doc.child(STR("node")).first_element_by_path(STR("child")) == xml_node());
 -	CHECK(doc.child(STR("node")).first_element_by_path(STR("child11")) == xml_node());
 -}
 -
 -struct test_walker: xml_tree_walker
 -{
 -	std::basic_string<pugi::char_t> log;
 -	unsigned int call_count;
 -	unsigned int stop_count;
 -
 -	test_walker(unsigned int stop_count = 0): call_count(0), stop_count(stop_count)
 -	{
 -	}
 -
 -	std::basic_string<pugi::char_t> depthstr() const
 -	{
 -		char buf[32];
 -		sprintf(buf, "%d", depth());
 -
 -	#ifdef PUGIXML_WCHAR_MODE
 -		wchar_t wbuf[32];
 -		std::copy(buf, buf + strlen(buf) + 1, &wbuf[0]);
 -
 -		return std::basic_string<pugi::char_t>(wbuf);
 -	#else
 -		return std::basic_string<pugi::char_t>(buf);
 -	#endif
 -	}
 -
 -	virtual bool begin(xml_node& node)
 -	{
 -		log += STR("|");
 -		log += depthstr();
 -		log += STR(" <");
 -		log += node.name();
 -		log += STR("=");
 -		log += node.value();
 -
 -		return ++call_count != stop_count && xml_tree_walker::begin(node);
 -	}
 -
 -	virtual bool for_each(xml_node& node)
 -	{
 -		log += STR("|");
 -		log += depthstr();
 -		log += STR(" !");
 -		log += node.name();
 -		log += STR("=");
 -		log += node.value();
 -
 -		return ++call_count != stop_count && xml_tree_walker::end(node);
 -	}
 -
 -	virtual bool end(xml_node& node)
 -	{
 -		log += STR("|");
 -		log += depthstr();
 -		log += STR(" >");
 -		log += node.name();
 -		log += STR("=");
 -		log += node.value();
 -
 -		return ++call_count != stop_count;
 -	}
 -};
 -
 -TEST_XML(dom_node_traverse, "<node><child>text</child></node>")
 -{
 -	test_walker walker;
 -
 -	CHECK(doc.traverse(walker));
 -
 -	CHECK(walker.call_count == 5);
 -	CHECK(walker.log == STR("|-1 <=|0 !node=|1 !child=|2 !=text|-1 >="));
 -}
 -
 -TEST_XML(dom_node_traverse_siblings, "<node><child/><child>text</child><child/></node>")
 -{
 -	test_walker walker;
 -
 -	CHECK(doc.traverse(walker));
 -
 -	CHECK(walker.call_count == 7);
 -	CHECK(walker.log == STR("|-1 <=|0 !node=|1 !child=|1 !child=|2 !=text|1 !child=|-1 >="));
 -}
 -
 -TEST(dom_node_traverse_empty)
 -{
 -	test_walker walker;
 -
 -	CHECK(xml_node().traverse(walker));
 -
 -	CHECK(walker.call_count == 2);
 -	CHECK(walker.log == STR("|-1 <=|-1 >="));
 -}
 -
 -TEST_XML(dom_node_traverse_child, "<node><child>text</child></node>")
 -{
 -	test_walker walker;
 -
 -	CHECK(doc.child(STR("node")).traverse(walker));
 -
 -	CHECK(walker.call_count == 4);
 -	CHECK(walker.log == STR("|-1 <node=|0 !child=|1 !=text|-1 >node="));
 -}
 -
 -TEST_XML(dom_node_traverse_stop_begin, "<node><child>text</child></node>")
 -{
 -	test_walker walker(1);
 -
 -	CHECK(!doc.traverse(walker));
 -
 -	CHECK(walker.call_count == 1);
 -	CHECK(walker.log == STR("|-1 <="));
 -}
 -
 -TEST_XML(dom_node_traverse_stop_for_each, "<node><child>text</child></node>")
 -{
 -	test_walker walker(3);
 -
 -	CHECK(!doc.traverse(walker));
 -
 -	CHECK(walker.call_count == 3);
 -	CHECK(walker.log == STR("|-1 <=|0 !node=|1 !child="));
 -}
 -
 -TEST_XML(dom_node_traverse_stop_end, "<node><child>text</child></node>")
 -{
 -	test_walker walker(5);
 -
 -	CHECK(!doc.traverse(walker));
 -
 -	CHECK(walker.call_count == 5);
 -	CHECK(walker.log == STR("|-1 <=|0 !node=|1 !child=|2 !=text|-1 >="));
 -}
 -
 -TEST_XML_FLAGS(dom_offset_debug, "<?xml?><?pi?><!--comment--><node>pcdata<![CDATA[cdata]]></node>", parse_default | parse_pi | parse_comments | parse_declaration)
 -{
 -	CHECK(xml_node().offset_debug() == -1);
 -	CHECK(doc.offset_debug() == 0);
 -
 -	xml_node_iterator it = doc.begin();
 -
 -	CHECK((it++)->offset_debug() == 2);
 -	CHECK((it++)->offset_debug() == 9);
 -	CHECK((it++)->offset_debug() == 17);
 -	CHECK((it++)->offset_debug() == 28);
 -
 -	xml_node_iterator cit = doc.child(STR("node")).begin();
 -	
 -	CHECK((cit++)->offset_debug() == 33);
 -	CHECK((cit++)->offset_debug() == 48);
 -}
 +#define _CRT_SECURE_NO_WARNINGS +#define _SCL_SECURE_NO_WARNINGS + +#include "common.hpp" + +#include <stdio.h> + +#include <string.h> +#include <wchar.h> + +#include <utility> +#include <vector> +#include <iterator> +#include <string> + +#include "helpers.hpp" + +#ifdef PUGIXML_NO_STL +template <typename I> static I move_iter(I base, int n) +{ +	if (n > 0) while (n--) ++base; +	else while (n++) --base; +	return base; +} +#else +template <typename I> static I move_iter(I base, int n) +{ +	std::advance(base, n); +	return base; +} +#endif + +template <typename T> static void generic_empty_test(const T& obj) +{ +	T null; + +	CHECK(null.empty()); +	CHECK(!obj.empty()); +} + +TEST_XML(dom_attr_bool_ops, "<node attr='1'/>") +{ +	generic_bool_ops_test(doc.child(STR("node")).attribute(STR("attr"))); +} + +TEST_XML(dom_attr_eq_ops, "<node attr1='1' attr2='2'/>") +{ +	generic_eq_ops_test(doc.child(STR("node")).attribute(STR("attr1")), doc.child(STR("node")).attribute(STR("attr2"))); +} + +TEST_XML(dom_attr_rel_ops, "<node attr1='1' attr2='2'/>") +{ +	generic_rel_ops_test(doc.child(STR("node")).attribute(STR("attr1")), doc.child(STR("node")).attribute(STR("attr2"))); +} + +TEST_XML(dom_attr_empty, "<node attr='1'/>") +{ +	generic_empty_test(doc.child(STR("node")).attribute(STR("attr"))); +} + +TEST_XML(dom_attr_next_previous_attribute, "<node attr1='1' attr2='2' />") +{ +	xml_attribute attr1 = doc.child(STR("node")).attribute(STR("attr1")); +	xml_attribute attr2 = doc.child(STR("node")).attribute(STR("attr2")); + +	CHECK(attr1.next_attribute() == attr2); +	CHECK(attr2.next_attribute() == xml_attribute()); +	 +	CHECK(attr1.previous_attribute() == xml_attribute()); +	CHECK(attr2.previous_attribute() == attr1); +	 +	CHECK(xml_attribute().next_attribute() == xml_attribute()); +	CHECK(xml_attribute().previous_attribute() == xml_attribute()); +} + +TEST_XML(dom_attr_name_value, "<node attr='1'/>") +{ +	xml_attribute attr = doc.child(STR("node")).attribute(STR("attr")); + +	CHECK_NAME_VALUE(attr, STR("attr"), STR("1")); +	CHECK_NAME_VALUE(xml_attribute(), STR(""), STR("")); +} + +TEST_XML(dom_attr_as_int, "<node attr1='1' attr2='-1' attr3='-2147483648' attr4='2147483647'/>") +{ +	xml_node node = doc.child(STR("node")); + +	CHECK(xml_attribute().as_int() == 0); +	CHECK(node.attribute(STR("attr1")).as_int() == 1); +	CHECK(node.attribute(STR("attr2")).as_int() == -1); +	CHECK(node.attribute(STR("attr3")).as_int() == -2147483647 - 1); +	CHECK(node.attribute(STR("attr4")).as_int() == 2147483647); +} + +TEST_XML(dom_attr_as_uint, "<node attr1='0' attr2='1' attr3='2147483647' attr4='4294967295'/>") +{ +	xml_node node = doc.child(STR("node")); + +	CHECK(xml_attribute().as_uint() == 0); +	CHECK(node.attribute(STR("attr1")).as_uint() == 0); +	CHECK(node.attribute(STR("attr2")).as_uint() == 1); +	CHECK(node.attribute(STR("attr3")).as_uint() == 2147483647); +	CHECK(node.attribute(STR("attr4")).as_uint() == 4294967295u); +} + +TEST_XML(dom_attr_as_float, "<node attr1='0' attr2='1' attr3='0.12' attr4='-5.1' attr5='3e-4' attr6='3.14159265358979323846'/>") +{ +	xml_node node = doc.child(STR("node")); + +	CHECK(xml_attribute().as_float() == 0); +	CHECK_DOUBLE(node.attribute(STR("attr1")).as_float(), 0); +	CHECK_DOUBLE(node.attribute(STR("attr2")).as_float(), 1); +	CHECK_DOUBLE(node.attribute(STR("attr3")).as_float(), 0.12); +	CHECK_DOUBLE(node.attribute(STR("attr4")).as_float(), -5.1); +	CHECK_DOUBLE(node.attribute(STR("attr5")).as_float(), 3e-4); +	CHECK_DOUBLE(node.attribute(STR("attr6")).as_float(), 3.14159265358979323846); +} + +TEST_XML(dom_attr_as_double, "<node attr1='0' attr2='1' attr3='0.12' attr4='-5.1' attr5='3e-4' attr6='3.14159265358979323846'/>") +{ +	xml_node node = doc.child(STR("node")); + +	CHECK(xml_attribute().as_double() == 0); +	CHECK_DOUBLE(node.attribute(STR("attr1")).as_double(), 0); +	CHECK_DOUBLE(node.attribute(STR("attr2")).as_double(), 1); +	CHECK_DOUBLE(node.attribute(STR("attr3")).as_double(), 0.12); +	CHECK_DOUBLE(node.attribute(STR("attr4")).as_double(), -5.1); +	CHECK_DOUBLE(node.attribute(STR("attr5")).as_double(), 3e-4); +	CHECK_DOUBLE(node.attribute(STR("attr6")).as_double(), 3.14159265358979323846); +} + +TEST_XML(dom_attr_as_bool, "<node attr1='0' attr2='1' attr3='true' attr4='True' attr5='Yes' attr6='yes' attr7='false'/>") +{ +	xml_node node = doc.child(STR("node")); + +	CHECK(!xml_attribute().as_bool()); +	CHECK(!node.attribute(STR("attr1")).as_bool()); +	CHECK(node.attribute(STR("attr2")).as_bool()); +	CHECK(node.attribute(STR("attr3")).as_bool()); +	CHECK(node.attribute(STR("attr4")).as_bool()); +	CHECK(node.attribute(STR("attr5")).as_bool()); +	CHECK(node.attribute(STR("attr6")).as_bool()); +	CHECK(!node.attribute(STR("attr7")).as_bool()); +} + +TEST_XML(dom_attr_iterator, "<node><node1 attr1='0'/><node2 attr1='0' attr2='1'/><node3/></node>") +{ +	xml_node node1 = doc.child(STR("node")).child(STR("node1")); +	xml_node node2 = doc.child(STR("node")).child(STR("node2")); +	xml_node node3 = doc.child(STR("node")).child(STR("node3")); + +	CHECK(xml_node().attributes_begin() == xml_attribute_iterator()); +	CHECK(xml_node().attributes_end() == xml_attribute_iterator()); + +	CHECK(node1.attributes_begin() == xml_attribute_iterator(node1.attribute(STR("attr1")), node1)); +	CHECK(move_iter(node1.attributes_begin(), 1) == node1.attributes_end()); +	CHECK(move_iter(node1.attributes_end(), -1) == node1.attributes_begin()); +	CHECK(*node1.attributes_begin() == node1.attribute(STR("attr1"))); +	CHECK_STRING(node1.attributes_begin()->name(), STR("attr1")); + +	CHECK(move_iter(node2.attributes_begin(), 2) == node2.attributes_end()); +	CHECK(move_iter(node2.attributes_end(), -2) == node2.attributes_begin()); + +	CHECK(node3.attributes_begin() != xml_attribute_iterator()); +	CHECK(node3.attributes_begin() == node3.attributes_end()); + +	xml_attribute_iterator it = xml_attribute_iterator(node2.attribute(STR("attr2")), node2); +	xml_attribute_iterator itt = it; + +	CHECK(itt++ == it); +	CHECK(itt == node2.attributes_end()); + +	CHECK(itt-- == node2.attributes_end()); +	CHECK(itt == it); + +	CHECK(++itt == node2.attributes_end()); +	CHECK(itt == node2.attributes_end()); + +	CHECK(--itt == it); +	CHECK(itt == it); + +	CHECK(++itt != it); +} + +TEST_XML(dom_attr_iterator_end, "<node><node1 attr1='0'/><node2 attr1='0' attr2='1'/><node3/></node>") +{ +	xml_node node1 = doc.child(STR("node")).child(STR("node1")); +	xml_node node2 = doc.child(STR("node")).child(STR("node2")); +	xml_node node3 = doc.child(STR("node")).child(STR("node3")); + +	CHECK(node1.attributes_end() != node2.attributes_end() && node1.attributes_end() != node3.attributes_end() && node2.attributes_end() != node3.attributes_end()); +	CHECK(node1.attributes_end() != xml_attribute_iterator() && node2.attributes_end() != xml_attribute_iterator() && node3.attributes_end() != xml_attribute_iterator()); +} + +TEST_XML(dom_attr_iterator_invalidate, "<node><node1 attr1='0'/><node2 attr1='0' attr2='1'/><node3/></node>") +{ +	xml_node node2 = doc.child(STR("node")).child(STR("node2")); + +	xml_attribute_iterator it1 = node2.attributes_begin(); +	xml_attribute_iterator it2 = move_iter(it1, 1); +	xml_attribute_iterator it3 = move_iter(it2, 1); + +	CHECK(it3 == node2.attributes_end()); + +	// removing attr2, it2 is invalid now, it3 is still past-the-end +	node2.remove_attribute(*it2); + +	CHECK(node2.attributes_end() == it3); +	CHECK(move_iter(it1, 1) == it3); +	CHECK(move_iter(it3, -1) == it1); +	CHECK_STRING(it1->name(), STR("attr1")); + +	// adding attr2 back, it3 is still past-the-end! +	xml_attribute_iterator it2new = xml_attribute_iterator(node2.append_attribute(STR("attr2-new")), node2); + +	CHECK(node2.attributes_end() == it3); +	CHECK(move_iter(it1, 1) == it2new); +	CHECK(move_iter(it2new, 1) == it3); +	CHECK(move_iter(it3, -1) == it2new); +	CHECK_STRING(it2new->name(), STR("attr2-new")); + +	// removing both attributes, it3 is now equal to the begin +	node2.remove_attribute(*it1); +	node2.remove_attribute(*it2new); +	CHECK(!node2.first_attribute()); + +	CHECK(node2.attributes_begin() == it3); +	CHECK(node2.attributes_end() == it3); +} + +TEST_XML(dom_node_bool_ops, "<node/>") +{ +	generic_bool_ops_test(doc.child(STR("node"))); +} + +TEST_XML(dom_node_eq_ops, "<node><node1/><node2/></node>") +{ +	generic_eq_ops_test(doc.child(STR("node")).child(STR("node1")), doc.child(STR("node")).child(STR("node2"))); +} + +TEST_XML(dom_node_rel_ops, "<node><node1/><node2/></node>") +{ +	generic_rel_ops_test(doc.child(STR("node")).child(STR("node1")), doc.child(STR("node")).child(STR("node2"))); +} + +TEST_XML(dom_node_empty, "<node/>") +{ +	generic_empty_test(doc.child(STR("node"))); +} + +TEST_XML(dom_node_iterator, "<node><node1><child1/></node1><node2><child1/><child2/></node2><node3/></node>") +{ +	xml_node node1 = doc.child(STR("node")).child(STR("node1")); +	xml_node node2 = doc.child(STR("node")).child(STR("node2")); +	xml_node node3 = doc.child(STR("node")).child(STR("node3")); + +	CHECK(xml_node().begin() == xml_node_iterator()); +	CHECK(xml_node().end() == xml_node_iterator()); + +	CHECK(node1.begin() == xml_node_iterator(node1.child(STR("child1")))); +	CHECK(move_iter(node1.begin(), 1) == node1.end()); +	CHECK(move_iter(node1.end(), -1) == node1.begin()); +	CHECK(*node1.begin() == node1.child(STR("child1"))); +	CHECK_STRING(node1.begin()->name(), STR("child1")); + +	CHECK(move_iter(node2.begin(), 2) == node2.end()); +	CHECK(move_iter(node2.end(), -2) == node2.begin()); + +	CHECK(node3.begin() != xml_node_iterator()); +	CHECK(node3.begin() == node3.end()); + +	xml_node_iterator it = node2.child(STR("child2")); +	xml_node_iterator itt = it; + +	CHECK(itt++ == it); +	CHECK(itt == node2.end()); + +	CHECK(itt-- == node2.end()); +	CHECK(itt == it); + +	CHECK(++itt == node2.end()); +	CHECK(itt == node2.end()); + +	CHECK(--itt == it); +	CHECK(itt == it); + +	CHECK(++itt != it); +} + +TEST_XML(dom_node_iterator_end, "<node><node1><child1/></node1><node2><child1/><child2/></node2><node3/></node>") +{ +	xml_node node1 = doc.child(STR("node")).child(STR("node1")); +	xml_node node2 = doc.child(STR("node")).child(STR("node2")); +	xml_node node3 = doc.child(STR("node")).child(STR("node3")); + +	CHECK(node1.end() != node2.end() && node1.end() != node3.end() && node2.end() != node3.end()); +	CHECK(node1.end() != xml_node_iterator() && node2.end() != xml_node_iterator() && node3.end() != xml_node_iterator()); +} + +TEST_XML(dom_node_iterator_invalidate, "<node><node1><child1/></node1><node2><child1/><child2/></node2><node3/></node>") +{ +	xml_node node2 = doc.child(STR("node")).child(STR("node2")); + +	xml_node_iterator it1 = node2.begin(); +	xml_node_iterator it2 = move_iter(it1, 1); +	xml_node_iterator it3 = move_iter(it2, 1); + +	CHECK(it3 == node2.end()); + +	// removing child2, it2 is invalid now, it3 is still past-the-end +	node2.remove_child(*it2); + +	CHECK(node2.end() == it3); +	CHECK(move_iter(it1, 1) == it3); +	CHECK(move_iter(it3, -1) == it1); +	CHECK_STRING(it1->name(), STR("child1")); + +	// adding attr2 back, it3 is still past-the-end! +	xml_node_iterator it2new = node2.append_child(); +	it2new->set_name(STR("child2-new")); + +	CHECK(node2.end() == it3); +	CHECK(move_iter(it1, 1) == it2new); +	CHECK(move_iter(it2new, 1) == it3); +	CHECK(move_iter(it3, -1) == it2new); +	CHECK_STRING(it2new->name(), STR("child2-new")); + +	// removing both nodes, it3 is now equal to the begin +	node2.remove_child(*it1); +	node2.remove_child(*it2new); +	CHECK(!node2.first_child()); + +	CHECK(node2.begin() == it3); +	CHECK(node2.end() == it3); +} + +TEST_XML(dom_node_parent, "<node><child/></node>") +{ +	CHECK(xml_node().parent() == xml_node()); +	CHECK(doc.child(STR("node")).child(STR("child")).parent() == doc.child(STR("node"))); +	CHECK(doc.child(STR("node")).parent() == doc); +} + +TEST_XML(dom_node_root, "<node><child/></node>") +{ +	CHECK(xml_node().root() == xml_node()); +	CHECK(doc.child(STR("node")).child(STR("child")).root() == doc); +	CHECK(doc.child(STR("node")).root() == doc); +} + +TEST_XML_FLAGS(dom_node_type, "<?xml?><?pi?><!--comment--><node>pcdata<![CDATA[cdata]]></node>", parse_default | parse_pi | parse_comments | parse_declaration) +{ +	CHECK(xml_node().type() == node_null); +	CHECK(doc.type() == node_document); + +	xml_node_iterator it = doc.begin(); + +	CHECK((it++)->type() == node_declaration); +	CHECK((it++)->type() == node_pi); +	CHECK((it++)->type() == node_comment); +	CHECK((it++)->type() == node_element); + +	xml_node_iterator cit = doc.child(STR("node")).begin(); +	 +	CHECK((cit++)->type() == node_pcdata); +	CHECK((cit++)->type() == node_cdata); +} + +TEST_XML_FLAGS(dom_node_name_value, "<?xml?><?pi?><!--comment--><node>pcdata<![CDATA[cdata]]></node>", parse_default | parse_pi | parse_comments | parse_declaration) +{ +	CHECK_NAME_VALUE(xml_node(), STR(""), STR("")); +	CHECK_NAME_VALUE(doc, STR(""), STR("")); + +	xml_node_iterator it = doc.begin(); + +	CHECK_NAME_VALUE(*it++, STR("xml"), STR("")); +	CHECK_NAME_VALUE(*it++, STR("pi"), STR("")); +	CHECK_NAME_VALUE(*it++, STR(""), STR("comment")); +	CHECK_NAME_VALUE(*it++, STR("node"), STR("")); + +	xml_node_iterator cit = doc.child(STR("node")).begin(); +	 +	CHECK_NAME_VALUE(*cit++, STR(""), STR("pcdata")); +	CHECK_NAME_VALUE(*cit++, STR(""), STR("cdata")); +} + +TEST_XML(dom_node_child, "<node><child1/><child2/></node>") +{ +	CHECK(xml_node().child(STR("n")) == xml_node()); + +	CHECK(doc.child(STR("n")) == xml_node()); +	CHECK_NAME_VALUE(doc.child(STR("node")), STR("node"), STR("")); +	CHECK(doc.child(STR("node")).child(STR("child2")) == doc.child(STR("node")).last_child()); +} + +TEST_XML(dom_node_attribute, "<node attr1='0' attr2='1'/>") +{ +	CHECK(xml_node().attribute(STR("a")) == xml_attribute()); + +	xml_node node = doc.child(STR("node")); + +	CHECK(node.attribute(STR("n")) == xml_attribute()); +	CHECK_NAME_VALUE(node.attribute(STR("attr1")), STR("attr1"), STR("0")); +	CHECK(node.attribute(STR("attr2")) == node.last_attribute()); +} + +TEST_XML(dom_node_next_previous_sibling, "<node><child1/><child2/><child3/></node>") +{ +	CHECK(xml_node().next_sibling() == xml_node()); +	CHECK(xml_node().next_sibling(STR("n")) == xml_node()); + +	CHECK(xml_node().previous_sibling() == xml_node()); +	CHECK(xml_node().previous_sibling(STR("n")) == xml_node()); + +	xml_node child1 = doc.child(STR("node")).child(STR("child1")); +	xml_node child2 = doc.child(STR("node")).child(STR("child2")); +	xml_node child3 = doc.child(STR("node")).child(STR("child3")); + +	CHECK(child1.next_sibling() == child2); +	CHECK(child3.next_sibling() == xml_node()); +	 +	CHECK(child1.previous_sibling() == xml_node()); +	CHECK(child3.previous_sibling() == child2); +	 +	CHECK(child1.next_sibling(STR("child3")) == child3); +	CHECK(child1.next_sibling(STR("child")) == xml_node()); + +	CHECK(child3.previous_sibling(STR("child1")) == child1); +	CHECK(child3.previous_sibling(STR("child")) == xml_node()); +} + +TEST_XML(dom_node_child_value, "<node><novalue/><child1>value1</child1><child2>value2<n/></child2><child3><![CDATA[value3]]></child3>value4</node>") +{ +	CHECK_STRING(xml_node().child_value(), STR("")); +	CHECK_STRING(xml_node().child_value(STR("n")), STR("")); + +	xml_node node = doc.child(STR("node")); + +	CHECK_STRING(node.child_value(), STR("value4")); +	CHECK_STRING(node.child(STR("child1")).child_value(), STR("value1")); +	CHECK_STRING(node.child(STR("child2")).child_value(), STR("value2")); +	CHECK_STRING(node.child(STR("child3")).child_value(), STR("value3")); +	CHECK_STRING(node.child_value(STR("child3")), STR("value3")); +} + +TEST_XML(dom_node_first_last_attribute, "<node attr1='0' attr2='1'/>") +{ +	xml_node node = doc.child(STR("node")); + +	CHECK(node.first_attribute() == node.attribute(STR("attr1"))); +	CHECK(node.last_attribute() == node.attribute(STR("attr2"))); + +	CHECK(xml_node().first_attribute() == xml_attribute()); +	CHECK(xml_node().last_attribute() == xml_attribute()); + +	CHECK(doc.first_attribute() == xml_attribute()); +	CHECK(doc.last_attribute() == xml_attribute()); +} + +TEST_XML(dom_node_first_last_child, "<node><child1/><child2/></node>") +{ +	xml_node node = doc.child(STR("node")); + +	CHECK(node.first_child() == node.child(STR("child1"))); +	CHECK(node.last_child() == node.child(STR("child2"))); + +	CHECK(xml_node().first_child() == xml_node()); +	CHECK(xml_node().last_child() == xml_node()); + +	CHECK(doc.first_child() == node); +	CHECK(doc.last_child() == node); +} + +TEST_XML(dom_node_find_child_by_attribute, "<node><child1 attr='value1'/><child2 attr='value2'/><child2 attr='value3'/></node>") +{ +	CHECK(xml_node().find_child_by_attribute(STR("name"), STR("attr"), STR("value")) == xml_node()); +	CHECK(xml_node().find_child_by_attribute(STR("attr"), STR("value")) == xml_node()); + +	xml_node node = doc.child(STR("node")); + +	CHECK(node.find_child_by_attribute(STR("child2"), STR("attr"), STR("value3")) == node.last_child()); +	CHECK(node.find_child_by_attribute(STR("child2"), STR("attr3"), STR("value3")) == xml_node()); +	CHECK(node.find_child_by_attribute(STR("attr"), STR("value2")) == node.child(STR("child2"))); +	CHECK(node.find_child_by_attribute(STR("attr3"), STR("value")) == xml_node()); +} + +struct find_predicate_const +{ +	bool result; + +	find_predicate_const(bool result): result(result) +	{ +	} + +	template <typename T> bool operator()(const T&) const +	{ +		return result; +	} +}; + +struct find_predicate_prefix +{ +	const pugi::char_t* prefix; + +	find_predicate_prefix(const pugi::char_t* prefix): prefix(prefix) +	{ +	} + +	template <typename T> bool operator()(const T& obj) const +	{ +	#ifdef PUGIXML_WCHAR_MODE +		// can't use wcsncmp here because of a bug in DMC +		return std::basic_string<pugi::char_t>(obj.name()).compare(0, wcslen(prefix), prefix) == 0; +	#else +		return strncmp(obj.name(), prefix, strlen(prefix)) == 0; +	#endif +	} +}; + +TEST_XML(dom_node_find_attribute, "<node attr1='0' attr2='1'/>") +{ +	CHECK(xml_node().find_attribute(find_predicate_const(true)) == xml_attribute()); + +	xml_node node = doc.child(STR("node")); + +	CHECK(doc.find_attribute(find_predicate_const(true)) == xml_attribute()); +	CHECK(node.find_attribute(find_predicate_const(true)) == node.first_attribute()); +	CHECK(node.find_attribute(find_predicate_const(false)) == xml_attribute()); +	CHECK(node.find_attribute(find_predicate_prefix(STR("attr2"))) == node.last_attribute()); +	CHECK(node.find_attribute(find_predicate_prefix(STR("attr"))) == node.first_attribute()); +} + +TEST_XML(dom_node_find_child, "<node><child1/><child2/></node>") +{ +	CHECK(xml_node().find_child(find_predicate_const(true)) == xml_node()); + +	xml_node node = doc.child(STR("node")); + +	CHECK(node.child(STR("node")).child(STR("child1")).find_child(find_predicate_const(true)) == xml_node()); +	CHECK(node.find_child(find_predicate_const(true)) == node.first_child()); +	CHECK(node.find_child(find_predicate_const(false)) == xml_node()); +	CHECK(node.find_child(find_predicate_prefix(STR("child2"))) == node.last_child()); +	CHECK(node.find_child(find_predicate_prefix(STR("child"))) == node.first_child()); +} + +TEST_XML(dom_node_find_node, "<node><child1/><child2/></node>") +{ +	CHECK(xml_node().find_node(find_predicate_const(true)) == xml_node()); + +	xml_node node = doc.child(STR("node")); + +	CHECK(node.child(STR("node")).child(STR("child1")).find_node(find_predicate_const(true)) == xml_node()); +	CHECK(node.find_node(find_predicate_const(true)) == node.first_child()); +	CHECK(node.find_node(find_predicate_const(false)) == xml_node()); +	CHECK(node.find_node(find_predicate_prefix(STR("child2"))) == node.last_child()); +	CHECK(node.find_node(find_predicate_prefix(STR("child"))) == node.first_child()); +	CHECK(doc.find_node(find_predicate_prefix(STR("child"))) == node.first_child()); +	CHECK(doc.find_node(find_predicate_prefix(STR("child2"))) == node.last_child()); +	CHECK(doc.find_node(find_predicate_prefix(STR("child3"))) == xml_node()); +} + +#ifndef PUGIXML_NO_STL +TEST_XML(dom_node_path, "<node><child1>text<child2/></child1></node>") +{ +	CHECK(xml_node().path() == STR("")); +	 +	CHECK(doc.path() == STR("")); +	CHECK(doc.child(STR("node")).path() == STR("/node")); +	CHECK(doc.child(STR("node")).child(STR("child1")).path() == STR("/node/child1")); +	CHECK(doc.child(STR("node")).child(STR("child1")).child(STR("child2")).path() == STR("/node/child1/child2")); +	CHECK(doc.child(STR("node")).child(STR("child1")).first_child().path() == STR("/node/child1/")); +	 +	CHECK(doc.child(STR("node")).child(STR("child1")).path('\\') == STR("\\node\\child1")); +} +#endif + +TEST_XML(dom_node_first_element_by_path, "<node><child1>text<child2/></child1></node>") +{ +	CHECK(xml_node().first_element_by_path(STR("/")) == xml_node()); +	 +	CHECK(doc.first_element_by_path(STR("")) == doc); +	CHECK(doc.first_element_by_path(STR("/")) == doc); + +	CHECK(doc.first_element_by_path(STR("/node/")) == doc.child(STR("node"))); +	CHECK(doc.first_element_by_path(STR("node/")) == doc.child(STR("node"))); +	CHECK(doc.first_element_by_path(STR("node")) == doc.child(STR("node"))); +	CHECK(doc.first_element_by_path(STR("/node")) == doc.child(STR("node"))); + +#ifndef PUGIXML_NO_STL +	CHECK(doc.first_element_by_path(STR("/node/child1/child2")).path() == STR("/node/child1/child2")); +#endif + +	CHECK(doc.first_element_by_path(STR("/node/child2")) == xml_node()); +	 +	CHECK(doc.first_element_by_path(STR("\\node\\child1"), '\\') == doc.child(STR("node")).child(STR("child1"))); + +	CHECK(doc.child(STR("node")).first_element_by_path(STR("..")) == doc); +	CHECK(doc.child(STR("node")).first_element_by_path(STR(".")) == doc.child(STR("node"))); + +	CHECK(doc.child(STR("node")).first_element_by_path(STR("../node/./child1/../.")) == doc.child(STR("node"))); + +	CHECK(doc.child(STR("node")).first_element_by_path(STR("child1")) == doc.child(STR("node")).child(STR("child1"))); +	CHECK(doc.child(STR("node")).first_element_by_path(STR("child1/")) == doc.child(STR("node")).child(STR("child1"))); +	CHECK(doc.child(STR("node")).first_element_by_path(STR("child")) == xml_node()); +	CHECK(doc.child(STR("node")).first_element_by_path(STR("child11")) == xml_node()); +} + +struct test_walker: xml_tree_walker +{ +	std::basic_string<pugi::char_t> log; +	unsigned int call_count; +	unsigned int stop_count; + +	test_walker(unsigned int stop_count = 0): call_count(0), stop_count(stop_count) +	{ +	} + +	std::basic_string<pugi::char_t> depthstr() const +	{ +		char buf[32]; +		sprintf(buf, "%d", depth()); + +	#ifdef PUGIXML_WCHAR_MODE +		wchar_t wbuf[32]; +		std::copy(buf, buf + strlen(buf) + 1, &wbuf[0]); + +		return std::basic_string<pugi::char_t>(wbuf); +	#else +		return std::basic_string<pugi::char_t>(buf); +	#endif +	} + +	virtual bool begin(xml_node& node) +	{ +		log += STR("|"); +		log += depthstr(); +		log += STR(" <"); +		log += node.name(); +		log += STR("="); +		log += node.value(); + +		return ++call_count != stop_count && xml_tree_walker::begin(node); +	} + +	virtual bool for_each(xml_node& node) +	{ +		log += STR("|"); +		log += depthstr(); +		log += STR(" !"); +		log += node.name(); +		log += STR("="); +		log += node.value(); + +		return ++call_count != stop_count && xml_tree_walker::end(node); +	} + +	virtual bool end(xml_node& node) +	{ +		log += STR("|"); +		log += depthstr(); +		log += STR(" >"); +		log += node.name(); +		log += STR("="); +		log += node.value(); + +		return ++call_count != stop_count; +	} +}; + +TEST_XML(dom_node_traverse, "<node><child>text</child></node>") +{ +	test_walker walker; + +	CHECK(doc.traverse(walker)); + +	CHECK(walker.call_count == 5); +	CHECK(walker.log == STR("|-1 <=|0 !node=|1 !child=|2 !=text|-1 >=")); +} + +TEST_XML(dom_node_traverse_siblings, "<node><child/><child>text</child><child/></node>") +{ +	test_walker walker; + +	CHECK(doc.traverse(walker)); + +	CHECK(walker.call_count == 7); +	CHECK(walker.log == STR("|-1 <=|0 !node=|1 !child=|1 !child=|2 !=text|1 !child=|-1 >=")); +} + +TEST(dom_node_traverse_empty) +{ +	test_walker walker; + +	CHECK(xml_node().traverse(walker)); + +	CHECK(walker.call_count == 2); +	CHECK(walker.log == STR("|-1 <=|-1 >=")); +} + +TEST_XML(dom_node_traverse_child, "<node><child>text</child></node>") +{ +	test_walker walker; + +	CHECK(doc.child(STR("node")).traverse(walker)); + +	CHECK(walker.call_count == 4); +	CHECK(walker.log == STR("|-1 <node=|0 !child=|1 !=text|-1 >node=")); +} + +TEST_XML(dom_node_traverse_stop_begin, "<node><child>text</child></node>") +{ +	test_walker walker(1); + +	CHECK(!doc.traverse(walker)); + +	CHECK(walker.call_count == 1); +	CHECK(walker.log == STR("|-1 <=")); +} + +TEST_XML(dom_node_traverse_stop_for_each, "<node><child>text</child></node>") +{ +	test_walker walker(3); + +	CHECK(!doc.traverse(walker)); + +	CHECK(walker.call_count == 3); +	CHECK(walker.log == STR("|-1 <=|0 !node=|1 !child=")); +} + +TEST_XML(dom_node_traverse_stop_end, "<node><child>text</child></node>") +{ +	test_walker walker(5); + +	CHECK(!doc.traverse(walker)); + +	CHECK(walker.call_count == 5); +	CHECK(walker.log == STR("|-1 <=|0 !node=|1 !child=|2 !=text|-1 >=")); +} + +TEST_XML_FLAGS(dom_offset_debug, "<?xml?><?pi?><!--comment--><node>pcdata<![CDATA[cdata]]></node>", parse_default | parse_pi | parse_comments | parse_declaration) +{ +	CHECK(xml_node().offset_debug() == -1); +	CHECK(doc.offset_debug() == 0); + +	xml_node_iterator it = doc.begin(); + +	CHECK((it++)->offset_debug() == 2); +	CHECK((it++)->offset_debug() == 9); +	CHECK((it++)->offset_debug() == 17); +	CHECK((it++)->offset_debug() == 28); + +	xml_node_iterator cit = doc.child(STR("node")).begin(); +	 +	CHECK((cit++)->offset_debug() == 33); +	CHECK((cit++)->offset_debug() == 48); +} diff --git a/tests/test_header_guard.cpp b/tests/test_header_guard.cpp index 3706cc9..2f65928 100644 --- a/tests/test_header_guard.cpp +++ b/tests/test_header_guard.cpp @@ -1,3 +1,3 @@ -// Tests header guards
 -#include "../src/pugixml.hpp"
 -#include "../src/pugixml.hpp"
 +// Tests header guards +#include "../src/pugixml.hpp" +#include "../src/pugixml.hpp" diff --git a/tests/test_header_iosfwd_1.cpp b/tests/test_header_iosfwd_1.cpp index 73e8527..0ed528a 100644 --- a/tests/test_header_iosfwd_1.cpp +++ b/tests/test_header_iosfwd_1.cpp @@ -1,3 +1,3 @@ -// Tests compatibility with iosfwd
 -#include "../src/pugixml.hpp"
 -#include <iosfwd>
 +// Tests compatibility with iosfwd +#include "../src/pugixml.hpp" +#include <iosfwd> diff --git a/tests/test_header_iosfwd_2.cpp b/tests/test_header_iosfwd_2.cpp index e472b9c..865d0d8 100644 --- a/tests/test_header_iosfwd_2.cpp +++ b/tests/test_header_iosfwd_2.cpp @@ -1,3 +1,3 @@ -// Tests compatibility with iosfwd
 -#include <iosfwd>
 -#include "../src/pugixml.hpp"
 +// Tests compatibility with iosfwd +#include <iosfwd> +#include "../src/pugixml.hpp" diff --git a/tests/test_header_iostream_1.cpp b/tests/test_header_iostream_1.cpp index 2b359f9..a836d4f 100644 --- a/tests/test_header_iostream_1.cpp +++ b/tests/test_header_iostream_1.cpp @@ -1,3 +1,3 @@ -// Tests compatibility with iostream
 -#include "../src/pugixml.hpp"
 -#include <iostream>
 +// Tests compatibility with iostream +#include "../src/pugixml.hpp" +#include <iostream> diff --git a/tests/test_header_iostream_2.cpp b/tests/test_header_iostream_2.cpp index 0b1b6b8..c0be50b 100644 --- a/tests/test_header_iostream_2.cpp +++ b/tests/test_header_iostream_2.cpp @@ -1,3 +1,3 @@ -// Tests compatibility with iostream
 -#include <iostream>
 -#include "../src/pugixml.hpp"
 +// Tests compatibility with iostream +#include <iostream> +#include "../src/pugixml.hpp" diff --git a/tests/test_header_string_1.cpp b/tests/test_header_string_1.cpp index 9e9d33f..07d1263 100644 --- a/tests/test_header_string_1.cpp +++ b/tests/test_header_string_1.cpp @@ -1,3 +1,3 @@ -// Tests compatibility with string
 -#include "../src/pugixml.hpp"
 -#include <string>
 +// Tests compatibility with string +#include "../src/pugixml.hpp" +#include <string> diff --git a/tests/test_header_string_2.cpp b/tests/test_header_string_2.cpp index 01d72ac..2813fc9 100644 --- a/tests/test_header_string_2.cpp +++ b/tests/test_header_string_2.cpp @@ -1,3 +1,3 @@ -// Tests compatibility with string
 -#include <string>
 -#include "../src/pugixml.hpp"
 +// Tests compatibility with string +#include <string> +#include "../src/pugixml.hpp" diff --git a/tests/test_memory.cpp b/tests/test_memory.cpp index 80e36c2..8b4b6bc 100644 --- a/tests/test_memory.cpp +++ b/tests/test_memory.cpp @@ -1,129 +1,129 @@ -#include "common.hpp"
 -
 -#include <string>
 -
 -namespace
 -{
 -	int allocate_count = 0;
 -	int deallocate_count = 0;
 -
 -	void* allocate(size_t size)
 -	{
 -		++allocate_count;
 -		return new char[size];
 -	}
 -
 -	void deallocate(void* ptr)
 -	{
 -		++deallocate_count;
 -		delete[] reinterpret_cast<char*>(ptr);
 -	}
 -}
 -
 -TEST(custom_memory_management)
 -{
 -	allocate_count = deallocate_count = 0;
 -
 -	// remember old functions
 -	allocation_function old_allocate = get_memory_allocation_function();
 -	deallocation_function old_deallocate = get_memory_deallocation_function();
 -
 -	// replace functions
 -	set_memory_management_functions(allocate, deallocate);
 -
 -	{
 -		// parse document
 -		xml_document doc;
 -
 -		CHECK(allocate_count == 0 && deallocate_count == 0);
 -
 -		CHECK(doc.load(STR("<node />")));
 -	
 -		CHECK(allocate_count == 2 && deallocate_count == 0);
 -
 -		// modify document (no new page)
 -		CHECK(doc.first_child().set_name(STR("foobars")));
 -		CHECK(allocate_count == 2 && deallocate_count == 0);
 -
 -		// modify document (new page)
 -		std::basic_string<pugi::char_t> s(65536, 'x');
 -
 -		CHECK(doc.first_child().set_name(s.c_str()));
 -		CHECK(allocate_count == 3 && deallocate_count == 0);
 -
 -		// modify document (new page, old one should die)
 -		s += s;
 -
 -		CHECK(doc.first_child().set_name(s.c_str()));
 -		CHECK(allocate_count == 4 && deallocate_count == 1);
 -	}
 -
 -	CHECK(allocate_count == 4 && deallocate_count == 4);
 -
 -	// restore old functions
 -	set_memory_management_functions(old_allocate, old_deallocate);
 -}
 -
 -TEST(large_allocations)
 -{
 -	allocate_count = deallocate_count = 0;
 -
 -	// remember old functions
 -	allocation_function old_allocate = get_memory_allocation_function();
 -	deallocation_function old_deallocate = get_memory_deallocation_function();
 -
 -	// replace functions
 -	set_memory_management_functions(allocate, deallocate);
 -
 -	{
 -		xml_document doc;
 -
 -		CHECK(allocate_count == 0 && deallocate_count == 0);
 -
 -		// initial fill
 -		for (size_t i = 0; i < 128; ++i)
 -		{
 -			std::basic_string<pugi::char_t> s(i * 128, 'x');
 -
 -			CHECK(doc.append_child(node_pcdata).set_value(s.c_str()));
 -		}
 -
 -		CHECK(allocate_count > 0 && deallocate_count == 0);
 -
 -		// grow-prune loop
 -		while (doc.first_child())
 -		{
 -			pugi::xml_node node;
 -
 -			// grow
 -			for (node = doc.first_child(); node; node = node.next_sibling())
 -			{
 -				std::basic_string<pugi::char_t> s = node.value();
 -
 -				CHECK(node.set_value((s + s).c_str()));
 -			}
 -
 -			// prune
 -			for (node = doc.first_child(); node; )
 -			{
 -				pugi::xml_node next = node.next_sibling().next_sibling();
 -
 -				node.parent().remove_child(node);
 -
 -				node = next;
 -			}
 -		}
 -
 -		CHECK(allocate_count == deallocate_count + 1); // only one live page left (it waits for new allocations)
 -
 -		char buffer;
 -		CHECK(doc.load_buffer_inplace(&buffer, 0, parse_default, get_native_encoding()));
 -
 -		CHECK(allocate_count == deallocate_count); // no live pages left
 -	}
 -
 -	CHECK(allocate_count == deallocate_count); // everything is freed
 -
 -	// restore old functions
 -	set_memory_management_functions(old_allocate, old_deallocate);
 -}
 +#include "common.hpp" + +#include <string> + +namespace +{ +	int allocate_count = 0; +	int deallocate_count = 0; + +	void* allocate(size_t size) +	{ +		++allocate_count; +		return new char[size]; +	} + +	void deallocate(void* ptr) +	{ +		++deallocate_count; +		delete[] reinterpret_cast<char*>(ptr); +	} +} + +TEST(custom_memory_management) +{ +	allocate_count = deallocate_count = 0; + +	// remember old functions +	allocation_function old_allocate = get_memory_allocation_function(); +	deallocation_function old_deallocate = get_memory_deallocation_function(); + +	// replace functions +	set_memory_management_functions(allocate, deallocate); + +	{ +		// parse document +		xml_document doc; + +		CHECK(allocate_count == 0 && deallocate_count == 0); + +		CHECK(doc.load(STR("<node />"))); +	 +		CHECK(allocate_count == 2 && deallocate_count == 0); + +		// modify document (no new page) +		CHECK(doc.first_child().set_name(STR("foobars"))); +		CHECK(allocate_count == 2 && deallocate_count == 0); + +		// modify document (new page) +		std::basic_string<pugi::char_t> s(65536, 'x'); + +		CHECK(doc.first_child().set_name(s.c_str())); +		CHECK(allocate_count == 3 && deallocate_count == 0); + +		// modify document (new page, old one should die) +		s += s; + +		CHECK(doc.first_child().set_name(s.c_str())); +		CHECK(allocate_count == 4 && deallocate_count == 1); +	} + +	CHECK(allocate_count == 4 && deallocate_count == 4); + +	// restore old functions +	set_memory_management_functions(old_allocate, old_deallocate); +} + +TEST(large_allocations) +{ +	allocate_count = deallocate_count = 0; + +	// remember old functions +	allocation_function old_allocate = get_memory_allocation_function(); +	deallocation_function old_deallocate = get_memory_deallocation_function(); + +	// replace functions +	set_memory_management_functions(allocate, deallocate); + +	{ +		xml_document doc; + +		CHECK(allocate_count == 0 && deallocate_count == 0); + +		// initial fill +		for (size_t i = 0; i < 128; ++i) +		{ +			std::basic_string<pugi::char_t> s(i * 128, 'x'); + +			CHECK(doc.append_child(node_pcdata).set_value(s.c_str())); +		} + +		CHECK(allocate_count > 0 && deallocate_count == 0); + +		// grow-prune loop +		while (doc.first_child()) +		{ +			pugi::xml_node node; + +			// grow +			for (node = doc.first_child(); node; node = node.next_sibling()) +			{ +				std::basic_string<pugi::char_t> s = node.value(); + +				CHECK(node.set_value((s + s).c_str())); +			} + +			// prune +			for (node = doc.first_child(); node; ) +			{ +				pugi::xml_node next = node.next_sibling().next_sibling(); + +				node.parent().remove_child(node); + +				node = next; +			} +		} + +		CHECK(allocate_count == deallocate_count + 1); // only one live page left (it waits for new allocations) + +		char buffer; +		CHECK(doc.load_buffer_inplace(&buffer, 0, parse_default, get_native_encoding())); + +		CHECK(allocate_count == deallocate_count); // no live pages left +	} + +	CHECK(allocate_count == deallocate_count); // everything is freed + +	// restore old functions +	set_memory_management_functions(old_allocate, old_deallocate); +} diff --git a/tests/test_parse.cpp b/tests/test_parse.cpp index 7f52d09..a997692 100644 --- a/tests/test_parse.cpp +++ b/tests/test_parse.cpp @@ -1,683 +1,683 @@ -#include "common.hpp"
 -
 -TEST(parse_pi_skip)
 -{
 -	xml_document doc;
 -
 -	unsigned int flag_sets[] = {parse_minimal, parse_minimal | parse_declaration};
 -
 -	for (unsigned int i = 0; i < sizeof(flag_sets) / sizeof(flag_sets[0]); ++i)
 -	{
 -		unsigned int flags = flag_sets[i];
 -
 -		CHECK(doc.load(STR("<?pi?><?pi value?>"), flags));
 -		CHECK(!doc.first_child());
 -
 -		CHECK(doc.load(STR("<?pi <tag/> value?>"), flags));
 -		CHECK(!doc.first_child());
 -	}
 -}
 -
 -TEST(parse_pi_parse)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<?pi1?><?pi2 value?>"), parse_minimal | parse_pi));
 -
 -	xml_node pi1 = doc.first_child();
 -	xml_node pi2 = doc.last_child();
 -
 -	CHECK(pi1 != pi2);
 -	CHECK(pi1.type() == node_pi);
 -	CHECK_STRING(pi1.name(), STR("pi1"));
 -	CHECK_STRING(pi1.value(), STR(""));
 -	CHECK(pi2.type() == node_pi);
 -	CHECK_STRING(pi2.name(), STR("pi2"));
 -	CHECK_STRING(pi2.value(), STR("value"));
 -}
 -
 -TEST(parse_pi_error)
 -{
 -	xml_document doc;
 -
 -	unsigned int flag_sets[] = {parse_minimal, parse_minimal | parse_pi};
 -
 -	for (unsigned int i = 0; i < sizeof(flag_sets) / sizeof(flag_sets[0]); ++i)
 -	{
 -		unsigned int flags = flag_sets[i];
 -
 -		CHECK(doc.load(STR("<?"), flags).status == status_bad_pi);
 -		CHECK(doc.load(STR("<??"), flags).status == status_bad_pi);
 -		CHECK(doc.load(STR("<?>"), flags).status == status_bad_pi);
 -		CHECK(doc.load(STR("<?#?>"), flags).status == status_bad_pi);
 -		CHECK(doc.load(STR("<?name"), flags).status == status_bad_pi);
 -		CHECK(doc.load(STR("<?name>"), flags).status == status_bad_pi);
 -		CHECK(doc.load(STR("<?name ?"), flags).status == status_bad_pi);
 -		CHECK(doc.load(STR("<?name?"), flags).status == status_bad_pi);
 -		CHECK(doc.load(STR("<?name? "), flags).status == status_bad_pi);
 -		CHECK(doc.load(STR("<?name?  "), flags).status == status_bad_pi);
 -		CHECK(doc.load(STR("<?name "), flags).status == status_bad_pi);
 -		CHECK(doc.load(STR("<?name  "), flags).status == status_bad_pi);
 -		CHECK(doc.load(STR("<?name   "), flags).status == status_bad_pi);
 -		CHECK(doc.load(STR("<?name value"), flags).status == status_bad_pi);
 -		CHECK(doc.load(STR("<?name value "), flags).status == status_bad_pi);
 -		CHECK(doc.load(STR("<?name value  "), flags).status == status_bad_pi);
 -		CHECK(doc.load(STR("<?name value  ?"), flags).status == status_bad_pi);
 -		CHECK(doc.load(STR("<?name value  ? "), flags).status == status_bad_pi);
 -		CHECK(doc.load(STR("<?name value  ? >"), flags).status == status_bad_pi);
 -		CHECK(doc.load(STR("<?name value  ? > "), flags).status == status_bad_pi);
 -		CHECK(doc.load(STR("<?name&"), flags).status == status_bad_pi);
 -		CHECK(doc.load(STR("<?name&?"), flags).status == status_bad_pi);
 -	}
 -	
 -	CHECK(doc.load(STR("<?xx#?>"), parse_minimal | parse_pi).status == status_bad_pi);
 -	CHECK(doc.load(STR("<?name&?>"), parse_minimal | parse_pi).status == status_bad_pi);
 -	CHECK(doc.load(STR("<?name& x?>"), parse_minimal | parse_pi).status == status_bad_pi);
 -}
 -
 -TEST(parse_comments_skip)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<!----><!--value-->"), parse_minimal));
 -	CHECK(!doc.first_child());
 -}
 -
 -TEST(parse_comments_parse)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<!----><!--value-->"), parse_minimal | parse_comments));
 -
 -	xml_node c1 = doc.first_child();
 -	xml_node c2 = doc.last_child();
 -
 -	CHECK(c1 != c2);
 -	CHECK(c1.type() == node_comment);
 -	CHECK_STRING(c1.name(), STR(""));
 -	CHECK_STRING(c1.value(), STR(""));
 -	CHECK(c2.type() == node_comment);
 -	CHECK_STRING(c2.name(), STR(""));
 -	CHECK_STRING(c2.value(), STR("value"));
 -}
 -
 -TEST(parse_comments_parse_no_eol)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<!--\r\rval1\rval2\r\nval3\nval4\r\r-->"), parse_minimal | parse_comments));
 -
 -	xml_node c = doc.first_child();
 -	CHECK(c.type() == node_comment);
 -	CHECK_STRING(c.value(), STR("\r\rval1\rval2\r\nval3\nval4\r\r"));
 -}
 -
 -TEST(parse_comments_parse_eol)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<!--\r\rval1\rval2\r\nval3\nval4\r\r-->"), parse_minimal | parse_comments | parse_eol));
 -
 -	xml_node c = doc.first_child();
 -	CHECK(c.type() == node_comment);
 -	CHECK_STRING(c.value(), STR("\n\nval1\nval2\nval3\nval4\n\n"));
 -}
 -
 -TEST(parse_comments_error)
 -{
 -	xml_document doc;
 -
 -	unsigned int flag_sets[] = {parse_minimal, parse_minimal | parse_comments, parse_minimal | parse_comments | parse_eol};
 -
 -	for (unsigned int i = 0; i < sizeof(flag_sets) / sizeof(flag_sets[0]); ++i)
 -	{
 -		unsigned int flags = flag_sets[i];
 -
 -		CHECK(doc.load(STR("<!-"), flags).status == status_bad_comment);
 -		CHECK(doc.load(STR("<!--"), flags).status == status_bad_comment);
 -		CHECK(doc.load(STR("<!--v"), flags).status == status_bad_comment);
 -		CHECK(doc.load(STR("<!-->"), flags).status == status_bad_comment);
 -		CHECK(doc.load(STR("<!--->"), flags).status == status_bad_comment);
 -		CHECK(doc.load(STR("<!-- <!-- --><!- -->"), flags).status == status_bad_comment);
 -	}
 -}
 -
 -TEST(parse_cdata_skip)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<![CDATA[]]><![CDATA[value]]>"), parse_minimal));
 -	CHECK(!doc.first_child());
 -}
 -
 -TEST(parse_cdata_parse)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<![CDATA[]]><![CDATA[value]]>"), parse_minimal | parse_cdata));
 -
 -	xml_node c1 = doc.first_child();
 -	xml_node c2 = doc.last_child();
 -
 -	CHECK(c1 != c2);
 -	CHECK(c1.type() == node_cdata);
 -	CHECK_STRING(c1.name(), STR(""));
 -	CHECK_STRING(c1.value(), STR(""));
 -	CHECK(c2.type() == node_cdata);
 -	CHECK_STRING(c2.name(), STR(""));
 -	CHECK_STRING(c2.value(), STR("value"));
 -}
 -
 -TEST(parse_cdata_parse_no_eol)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<![CDATA[\r\rval1\rval2\r\nval3\nval4\r\r]]>"), parse_minimal | parse_cdata));
 -
 -	xml_node c = doc.first_child();
 -	CHECK(c.type() == node_cdata);
 -	CHECK_STRING(c.value(), STR("\r\rval1\rval2\r\nval3\nval4\r\r"));
 -}
 -
 -TEST(parse_cdata_parse_eol)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<![CDATA[\r\rval1\rval2\r\nval3\nval4\r\r]]>"), parse_minimal | parse_cdata | parse_eol));
 -
 -	xml_node c = doc.first_child();
 -	CHECK(c.type() == node_cdata);
 -	CHECK_STRING(c.value(), STR("\n\nval1\nval2\nval3\nval4\n\n"));
 -}
 -
 -TEST(parse_cdata_error)
 -{
 -	xml_document doc;
 -
 -	unsigned int flag_sets[] = {parse_minimal, parse_minimal | parse_cdata, parse_minimal | parse_cdata | parse_eol};
 -
 -	for (unsigned int i = 0; i < sizeof(flag_sets) / sizeof(flag_sets[0]); ++i)
 -	{
 -		unsigned int flags = flag_sets[i];
 -
 -		CHECK(doc.load(STR("<!["), flags).status == status_bad_cdata);
 -		CHECK(doc.load(STR("<![C"), flags).status == status_bad_cdata);
 -		CHECK(doc.load(STR("<![CD"), flags).status == status_bad_cdata);
 -		CHECK(doc.load(STR("<![CDA"), flags).status == status_bad_cdata);
 -		CHECK(doc.load(STR("<![CDAT"), flags).status == status_bad_cdata);
 -		CHECK(doc.load(STR("<![CDATA"), flags).status == status_bad_cdata);
 -		CHECK(doc.load(STR("<![CDATA["), flags).status == status_bad_cdata);
 -		CHECK(doc.load(STR("<![CDATA[]"), flags).status == status_bad_cdata);
 -		CHECK(doc.load(STR("<![CDATA[data"), flags).status == status_bad_cdata);
 -		CHECK(doc.load(STR("<![CDATA[data]"), flags).status == status_bad_cdata);
 -		CHECK(doc.load(STR("<![CDATA[data]]"), flags).status == status_bad_cdata);
 -		CHECK(doc.load(STR("<![CDATA[>"), flags).status == status_bad_cdata);
 -		CHECK(doc.load(STR("<![CDATA[ <![CDATA[]]><![CDATA ]]>"), flags).status == status_bad_cdata);
 -	}
 -}
 -
 -TEST(parse_ws_pcdata_skip)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("  "), parse_minimal));
 -	CHECK(!doc.first_child());
 -
 -	CHECK(doc.load(STR("<root>  <node>  </node>  </root>"), parse_minimal));
 -	
 -	xml_node root = doc.child(STR("root"));
 -	
 -	CHECK(root.first_child() == root.last_child());
 -	CHECK(!root.first_child().first_child());
 -}
 -
 -TEST(parse_ws_pcdata_parse)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<root>  <node>  </node>  </root>"), parse_minimal | parse_ws_pcdata));
 -
 -	xml_node root = doc.child(STR("root"));
 -
 -	xml_node c1 = root.first_child();
 -	xml_node c2 = c1.next_sibling();
 -	xml_node c3 = c2.next_sibling();
 -
 -	CHECK(c3 == root.last_child());
 -
 -	CHECK(c1.type() == node_pcdata);
 -	CHECK_STRING(c1.value(), STR("  "));
 -	CHECK(c3.type() == node_pcdata);
 -	CHECK_STRING(c3.value(), STR("  "));
 -
 -	CHECK(c2.first_child() == c2.last_child());
 -	CHECK(c2.first_child().type() == node_pcdata);
 -	CHECK_STRING(c2.first_child().value(), STR("  "));
 -}
 -
 -TEST(parse_pcdata_no_eol)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<root>\r\rval1\rval2\r\nval3\nval4\r\r</root>"), parse_minimal));
 -
 -	CHECK_STRING(doc.child_value(STR("root")), STR("\r\rval1\rval2\r\nval3\nval4\r\r"));
 -}
 -
 -TEST(parse_pcdata_eol)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<root>\r\rval1\rval2\r\nval3\nval4\r\r</root>"), parse_minimal | parse_eol));
 -
 -	CHECK_STRING(doc.child_value(STR("root")), STR("\n\nval1\nval2\nval3\nval4\n\n"));
 -}
 -
 -TEST(parse_pcdata_skip_ext)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("pre<root/>post"), parse_minimal));
 -	CHECK(doc.first_child() == doc.last_child());
 -	CHECK(doc.first_child().type() == node_element);
 -}
 -
 -TEST(parse_pcdata_error)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<root>pcdata"), parse_minimal).status == status_end_element_mismatch);
 -}
 -
 -TEST(parse_escapes_skip)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<node id='<>&'"'><>&'"</node>"), parse_minimal));
 -	CHECK_STRING(doc.child(STR("node")).attribute(STR("id")).value(), STR("<>&'""));
 -}
 -
 -TEST(parse_escapes_parse)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<node id='<>&'"'><>&'"</node>"), parse_minimal | parse_escapes));
 -	CHECK_STRING(doc.child_value(STR("node")), STR("<>&'\""));
 -	CHECK_STRING(doc.child(STR("node")).attribute(STR("id")).value(), STR("<>&'\""));
 -}
 -
 -TEST(parse_escapes_code)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<node>  </node>"), parse_minimal | parse_escapes));
 -	CHECK_STRING(doc.child_value(STR("node")), STR("\01  "));
 -}
 -
 -TEST(parse_escapes_code_exhaustive_dec)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<node>&#/;	&#:;&#a;&#A;
</node>"), parse_minimal | parse_escapes));
 -	CHECK_STRING(doc.child_value(STR("node")), STR("&#/;\x1\x2\x3\x4\x5\x6\x7\x8\x9&#:;&#a;&#A;
"));
 -}
 -
 -TEST(parse_escapes_code_exhaustive_hex)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<node>&#x/;	&#x:;&#x@;

&#xG;&#x`;

&#xg;</node>"), parse_minimal | parse_escapes));
 -	CHECK_STRING(doc.child_value(STR("node")), STR("&#x/;\x1\x2\x3\x4\x5\x6\x7\x8\x9&#x:;&#x@;\xa\xb\xc\xd\xe\xf&#xG;&#x`;\xa\xb\xc\xd\xe\xf&#xg;"));
 -}
 -
 -TEST(parse_escapes_code_restore)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<node>  - - </node>"), parse_minimal | parse_escapes));
 -	CHECK_STRING(doc.child_value(STR("node")), STR("  - - "));
 -}
 -
 -TEST(parse_escapes_char_restore)
 -{
 -	xml_document doc;
 -
 -	CHECK(doc.load(STR("<node>&q &qu &quo " </node>"), parse_minimal | parse_escapes));
 -	CHECK_STRING(doc.child_value(STR("node")), STR("&q &qu &quo " "));
 -
 -	CHECK(doc.load(STR("<node>&a &ap &apo &apos </node>"), parse_minimal | parse_escapes));
 -	CHECK_STRING(doc.child_value(STR("node")), STR("&a &ap &apo &apos "));
 -
 -	CHECK(doc.load(STR("<node>&a &am & </node>"), parse_minimal | parse_escapes));
 -	CHECK_STRING(doc.child_value(STR("node")), STR("&a &am & "));
 -
 -	CHECK(doc.load(STR("<node>&l < </node>"), parse_minimal | parse_escapes));
 -	CHECK_STRING(doc.child_value(STR("node")), STR("&l < "));
 -
 -	CHECK(doc.load(STR("<node>&g > </node>"), parse_minimal | parse_escapes));
 -	CHECK_STRING(doc.child_value(STR("node")), STR("&g > "));
 -}
 -
 -TEST(parse_escapes_unicode)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<node>γγ𤭢</node>"), parse_minimal | parse_escapes));
 -
 -#ifdef PUGIXML_WCHAR_MODE
 -	const pugi::char_t* v = doc.child_value(STR("node"));
 -
 -	unsigned int v2 = v[2];
 -	size_t wcharsize = sizeof(wchar_t);
 -
 -	CHECK(v[0] == 0x3b3 && v[1] == 0x3b3 && (wcharsize == 2 ? v[2] == 0xd852 && v[3] == 0xdf62 : v2 == 0x24b62));
 -#else
 -	CHECK_STRING(doc.child_value(STR("node")), "\xce\xb3\xce\xb3\xf0\xa4\xad\xa2");
 -#endif
 -}
 -
 -TEST(parse_escapes_error)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<node>g;&#ab;"</node>"), parse_minimal | parse_escapes));
 -	CHECK_STRING(doc.child_value(STR("node")), STR("g;&#ab;""));
 -
 -	CHECK(!doc.load(STR("<node id='")));
 -	CHECK(!doc.load(STR("<node id='&g")));
 -	CHECK(!doc.load(STR("<node id='>")));
 -	CHECK(!doc.load(STR("<node id='&l")));
 -	CHECK(!doc.load(STR("<node id='<")));
 -	CHECK(!doc.load(STR("<node id='&a")));
 -	CHECK(!doc.load(STR("<node id='&")));
 -	CHECK(!doc.load(STR("<node id='&apos")));
 -}
 -
 -TEST(parse_escapes_code_invalid)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<node>&#;&#x;&;&#x-;&#-;</node>"), parse_minimal | parse_escapes));
 -	CHECK_STRING(doc.child_value(STR("node")), STR("&#;&#x;&;&#x-;&#-;"));
 -}
 -
 -TEST(parse_attribute_spaces)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<node id1='v1' id2 ='v2' id3= 'v3' id4 = 'v4' id5 \n\r\t = \r\t\n 'v5' />"), parse_minimal));
 -	CHECK_STRING(doc.child(STR("node")).attribute(STR("id1")).value(), STR("v1"));
 -	CHECK_STRING(doc.child(STR("node")).attribute(STR("id2")).value(), STR("v2"));
 -	CHECK_STRING(doc.child(STR("node")).attribute(STR("id3")).value(), STR("v3"));
 -	CHECK_STRING(doc.child(STR("node")).attribute(STR("id4")).value(), STR("v4"));
 -	CHECK_STRING(doc.child(STR("node")).attribute(STR("id5")).value(), STR("v5"));
 -}
 -
 -TEST(parse_attribute_quot)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<node id1='v1' id2=\"v2\"/>"), parse_minimal));
 -	CHECK_STRING(doc.child(STR("node")).attribute(STR("id1")).value(), STR("v1"));
 -	CHECK_STRING(doc.child(STR("node")).attribute(STR("id2")).value(), STR("v2"));
 -}
 -
 -TEST(parse_attribute_no_eol_no_wconv)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<node id=' \t\r\rval1  \rval2\r\nval3\nval4\r\r'/>"), parse_minimal));
 -	CHECK_STRING(doc.child(STR("node")).attribute(STR("id")).value(), STR(" \t\r\rval1  \rval2\r\nval3\nval4\r\r"));
 -}
 -
 -TEST(parse_attribute_eol_no_wconv)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<node id=' \t\r\rval1  \rval2\r\nval3\nval4\r\r'/>"), parse_minimal | parse_eol));
 -	CHECK_STRING(doc.child(STR("node")).attribute(STR("id")).value(), STR(" \t\n\nval1  \nval2\nval3\nval4\n\n"));
 -}
 -
 -TEST(parse_attribute_no_eol_wconv)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<node id=' \t\r\rval1  \rval2\r\nval3\nval4\r\r'/>"), parse_minimal | parse_wconv_attribute));
 -	CHECK_STRING(doc.child(STR("node")).attribute(STR("id")).value(), STR("    val1   val2 val3 val4  "));
 -}
 -
 -TEST(parse_attribute_eol_wconv)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<node id=' \t\r\rval1  \rval2\r\nval3\nval4\r\r'/>"), parse_minimal | parse_eol | parse_wconv_attribute));
 -	CHECK_STRING(doc.child(STR("node")).attribute(STR("id")).value(), STR("    val1   val2 val3 val4  "));
 -}
 -
 -TEST(parse_attribute_wnorm)
 -{
 -	xml_document doc;
 -
 -	for (int eol = 0; eol < 2; ++eol)
 -		for (int wconv = 0; wconv < 2; ++wconv)
 -		{
 -			unsigned int flags = parse_minimal | parse_wnorm_attribute | (eol ? parse_eol : 0) | (wconv ? parse_wconv_attribute : 0);
 -			CHECK(doc.load(STR("<node id=' \t\r\rval1  \rval2\r\nval3\nval4\r\r'/>"), flags));
 -			CHECK_STRING(doc.child(STR("node")).attribute(STR("id")).value(), STR("val1 val2 val3 val4"));
 -		}
 -}
 -
 -TEST(parse_attribute_variations)
 -{
 -	xml_document doc;
 -
 -	for (int wnorm = 0; wnorm < 2; ++wnorm)
 -		for (int eol = 0; eol < 2; ++eol)
 -			for (int wconv = 0; wconv < 2; ++wconv)
 -				for (int escapes = 0; escapes < 2; ++escapes)
 -				{
 -					unsigned int flags = parse_minimal;
 -					
 -					 flags |= (wnorm ? parse_wnorm_attribute : 0);
 -					 flags |= (eol ? parse_eol : 0);
 -					 flags |= (wconv ? parse_wconv_attribute : 0);
 -					 flags |= (escapes ? parse_escapes : 0);
 -
 -					CHECK(doc.load(STR("<node id='1'/>"), flags));
 -					CHECK_STRING(doc.child(STR("node")).attribute(STR("id")).value(), STR("1"));
 -				}
 -}
 -
 -
 -TEST(parse_attribute_error)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<node id"), parse_minimal).status == status_bad_attribute);
 -	CHECK(doc.load(STR("<node id "), parse_minimal).status == status_bad_attribute);
 -	CHECK(doc.load(STR("<node id  "), parse_minimal).status == status_bad_attribute);
 -	CHECK(doc.load(STR("<node id   "), parse_minimal).status == status_bad_attribute);
 -	CHECK(doc.load(STR("<node id/"), parse_minimal).status == status_bad_attribute);
 -	CHECK(doc.load(STR("<node id/>"), parse_minimal).status == status_bad_attribute);
 -	CHECK(doc.load(STR("<node id?/>"), parse_minimal).status == status_bad_attribute);
 -	CHECK(doc.load(STR("<node id=/>"), parse_minimal).status == status_bad_attribute);
 -	CHECK(doc.load(STR("<node id='/>"), parse_minimal).status == status_bad_attribute);
 -	CHECK(doc.load(STR("<node id=\"/>"), parse_minimal).status == status_bad_attribute);
 -	CHECK(doc.load(STR("<node id=\"'/>"), parse_minimal).status == status_bad_attribute);
 -	CHECK(doc.load(STR("<node id='\"/>"), parse_minimal).status == status_bad_attribute);
 -	CHECK(doc.load(STR("<node id='\"/>"), parse_minimal).status == status_bad_attribute);
 -	CHECK(doc.load(STR("<node #/>"), parse_minimal).status == status_bad_start_element);
 -	CHECK(doc.load(STR("<node#/>"), parse_minimal).status == status_bad_start_element);
 -	CHECK(doc.load(STR("<node id1='1'id2='2'/>"), parse_minimal).status == status_bad_attribute);
 -	CHECK(doc.load(STR("<node id&='1'/>"), parse_minimal).status == status_bad_attribute);
 -	CHECK(doc.load(STR("<node &='1'/>"), parse_minimal).status == status_bad_start_element);
 -}
 -
 -TEST(parse_tag_single)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<node/><node /><node\n/>"), parse_minimal));
 -	CHECK_NODE(doc, STR("<node /><node /><node />"));
 -}
 -
 -TEST(parse_tag_hierarchy)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<node><n1><n2/></n1><n3><n4><n5></n5></n4></n3 \r\n></node>"), parse_minimal));
 -	CHECK_NODE(doc, STR("<node><n1><n2 /></n1><n3><n4><n5 /></n4></n3></node>"));
 -}
 -
 -TEST(parse_tag_error)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<"), parse_minimal).status == status_unrecognized_tag);
 -	CHECK(doc.load(STR("<!"), parse_minimal).status == status_unrecognized_tag);
 -	CHECK(doc.load(STR("<!D"), parse_minimal).status == status_unrecognized_tag);
 -	CHECK(doc.load(STR("<#"), parse_minimal).status == status_unrecognized_tag);
 -	CHECK(doc.load(STR("<node#"), parse_minimal).status == status_bad_start_element);
 -	CHECK(doc.load(STR("<node"), parse_minimal).status == status_bad_start_element);
 -	CHECK(doc.load(STR("<node/"), parse_minimal).status == status_bad_start_element);
 -	CHECK(doc.load(STR("<node /"), parse_minimal).status == status_bad_start_element);
 -	CHECK(doc.load(STR("<node / "), parse_minimal).status == status_bad_start_element);
 -	CHECK(doc.load(STR("<node / >"), parse_minimal).status == status_bad_start_element);
 -	CHECK(doc.load(STR("<node/ >"), parse_minimal).status == status_bad_start_element);
 -	CHECK(doc.load(STR("</ node>"), parse_minimal).status == status_end_element_mismatch);
 -	CHECK(doc.load(STR("</node"), parse_minimal).status == status_end_element_mismatch);
 -	CHECK(doc.load(STR("</node "), parse_minimal).status == status_end_element_mismatch);
 -	CHECK(doc.load(STR("<node></ node>"), parse_minimal).status == status_end_element_mismatch);
 -	CHECK(doc.load(STR("<node></node"), parse_minimal).status == status_bad_end_element);
 -	CHECK(doc.load(STR("<node></node "), parse_minimal).status == status_bad_end_element);
 -	CHECK(doc.load(STR("<node></nodes>"), parse_minimal).status == status_end_element_mismatch);
 -	CHECK(doc.load(STR("<node>"), parse_minimal).status == status_end_element_mismatch);
 -	CHECK(doc.load(STR("<node/><"), parse_minimal).status == status_unrecognized_tag);
 -	CHECK(doc.load(STR("<node attr='value'>"), parse_minimal).status == status_end_element_mismatch);
 -	CHECK(doc.load(STR("</></node>"), parse_minimal).status == status_end_element_mismatch);
 -	CHECK(doc.load(STR("</node>"), parse_minimal).status == status_end_element_mismatch);
 -	CHECK(doc.load(STR("</>"), parse_minimal).status == status_end_element_mismatch);
 -	CHECK(doc.load(STR("<node></node v>"), parse_minimal).status == status_bad_end_element);
 -	CHECK(doc.load(STR("<node&/>"), parse_minimal).status == status_bad_start_element);
 -	CHECK(doc.load(STR("<node& v='1'/>"), parse_minimal).status == status_bad_start_element);
 -}
 -
 -TEST(parse_declaration_cases)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<?xml?><?xmL?><?xMl?><?xML?><?Xml?><?XmL?><?XMl?><?XML?>"), parse_minimal | parse_pi));
 -	CHECK(!doc.first_child());
 -}
 -
 -TEST(parse_declaration_attr_cases)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<?xml ?><?xmL ?><?xMl ?><?xML ?><?Xml ?><?XmL ?><?XMl ?><?XML ?>"), parse_minimal | parse_pi));
 -	CHECK(!doc.first_child());
 -}
 -
 -TEST(parse_declaration_skip)
 -{
 -	xml_document doc;
 -
 -	unsigned int flag_sets[] = {parse_minimal, parse_minimal | parse_pi};
 -
 -	for (unsigned int i = 0; i < sizeof(flag_sets) / sizeof(flag_sets[0]); ++i)
 -	{
 -		unsigned int flags = flag_sets[i];
 -
 -		CHECK(doc.load(STR("<?xml?><?xml version='1.0'?>"), flags));
 -		CHECK(!doc.first_child());
 -
 -		CHECK(doc.load(STR("<?xml <tag/> ?>"), flags));
 -		CHECK(!doc.first_child());
 -	}
 -}
 -
 -TEST(parse_declaration_parse)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("<?xml?><?xml version='1.0'?>"), parse_minimal | parse_declaration));
 -
 -	xml_node d1 = doc.first_child();
 -	xml_node d2 = doc.last_child();
 -
 -	CHECK(d1 != d2);
 -	CHECK(d1.type() == node_declaration);
 -	CHECK_STRING(d1.name(), STR("xml"));
 -	CHECK(d2.type() == node_declaration);
 -	CHECK_STRING(d2.name(), STR("xml"));
 -	CHECK_STRING(d2.attribute(STR("version")).value(), STR("1.0"));
 -}
 -
 -TEST(parse_declaration_error)
 -{
 -	xml_document doc;
 -
 -	unsigned int flag_sets[] = {parse_minimal, parse_minimal | parse_declaration};
 -
 -	for (unsigned int i = 0; i < sizeof(flag_sets) / sizeof(flag_sets[0]); ++i)
 -	{
 -		unsigned int flags = flag_sets[i];
 -
 -		CHECK(doc.load(STR("<?xml"), flags).status == status_bad_pi);
 -		CHECK(doc.load(STR("<?xml?"), flags).status == status_bad_pi);
 -		CHECK(doc.load(STR("<?xml>"), flags).status == status_bad_pi);
 -		CHECK(doc.load(STR("<?xml version='1>"), flags).status == status_bad_pi);
 -	}
 -	
 -	CHECK(doc.load(STR("<?xml version='1?>"), parse_minimal | parse_declaration).status == status_bad_attribute);
 -	CHECK(doc.load(STR("<foo><?xml version='1'?></foo>"), parse_minimal | parse_declaration).status == status_bad_pi);
 -}
 -
 -TEST(parse_empty)
 -{
 -	xml_document doc;
 -	CHECK(doc.load(STR("")) && !doc.first_child());
 -}
 -
 -TEST(parse_out_of_memory)
 -{
 -	test_runner::_memory_fail_threshold = 256;
 -
 -	xml_document doc;
 -	CHECK(doc.load(STR("<foo a='1'/>")).status == status_out_of_memory);
 -	CHECK(!doc.first_child());
 -}
 -
 -TEST(parse_out_of_memory_halfway)
 -{
 -	unsigned int count = 10000;
 -	char_t* text = new char_t[count * 4];
 -
 -	for (unsigned int i = 0; i < count; ++i)
 -	{
 -		text[4*i + 0] = '<';
 -		text[4*i + 1] = 'n';
 -		text[4*i + 2] = '/';
 -		text[4*i + 3] = '>';
 -	}
 -
 -	test_runner::_memory_fail_threshold = 65536;
 -
 -	xml_document doc;
 -	CHECK(doc.load_buffer_inplace(text, count * 4).status == status_out_of_memory);
 -	CHECK_NODE(doc.first_child(), STR("<n />"));
 -
 -	delete[] text;
 -}
 -
 -static bool test_offset(const char_t* contents, unsigned int options, pugi::xml_parse_status status, ptrdiff_t offset)
 -{
 -	xml_document doc;
 -	xml_parse_result res = doc.load(contents, options);
 -
 -	return res.status == status && res.offset == offset;
 -}
 -
 -#define CHECK_OFFSET(contents, options, status, offset) CHECK(test_offset(STR(contents), options, status, offset))
 -
 -TEST(parse_error_offset)
 -{
 -	CHECK_OFFSET("<node/>", parse_default, status_ok, 0);
 -
 -	test_runner::_memory_fail_threshold = 1;
 -	CHECK_OFFSET("<node/>", parse_default, status_out_of_memory, 0);
 -	test_runner::_memory_fail_threshold = 0;
 -
 -	CHECK_OFFSET("<3d/>", parse_default, status_unrecognized_tag, 1);
 -	CHECK_OFFSET(" <3d/>", parse_default, status_unrecognized_tag, 2);
 -	CHECK_OFFSET(" <", parse_default, status_unrecognized_tag, 2);
 -
 -	CHECK_OFFSET("<?pi", parse_default, status_bad_pi, 3);
 -	CHECK_OFFSET("<?pi", parse_default | parse_pi, status_bad_pi, 3);
 -	CHECK_OFFSET("<?xml", parse_default | parse_declaration, status_bad_pi, 4);
 -
 -	CHECK_OFFSET("<!----", parse_default, status_bad_comment, 5);
 -	CHECK_OFFSET("<!----", parse_default | parse_comments, status_bad_comment, 4);
 -
 -	CHECK_OFFSET("<![CDA", parse_default, status_bad_cdata, 5);
 -	CHECK_OFFSET("<![CDATA[non-terminated]]", parse_default, status_bad_cdata, 9);
 -
 -	CHECK_OFFSET("<!DOCTYPE doc", parse_default, status_bad_doctype, 12);
 -	CHECK_OFFSET("<!DOCTYPE greeting [ <!ATTLIST list type    (bullets|ordered|glossary)  \"orde", parse_default, status_bad_doctype, 76);
 -
 -	CHECK_OFFSET("<node", parse_default, status_bad_start_element, 4);
 -	CHECK_OFFSET("<node ", parse_default, status_bad_start_element, 5);
 -	CHECK_OFFSET("<nod%>", parse_default, status_bad_start_element, 5);
 -
 -	CHECK_OFFSET("<node a=2>", parse_default, status_bad_attribute, 8);
 -	CHECK_OFFSET("<node a='2>", parse_default, status_bad_attribute, 9);
 -
 -	CHECK_OFFSET("<n></n $>", parse_default, status_bad_end_element, 7);
 -	CHECK_OFFSET("<n></n", parse_default, status_bad_end_element, 5);
 -
 -	CHECK_OFFSET("<no></na>", parse_default, status_end_element_mismatch, 8);
 -	CHECK_OFFSET("<no></nod>", parse_default, status_end_element_mismatch, 9);
 -}
 +#include "common.hpp" + +TEST(parse_pi_skip) +{ +	xml_document doc; + +	unsigned int flag_sets[] = {parse_minimal, parse_minimal | parse_declaration}; + +	for (unsigned int i = 0; i < sizeof(flag_sets) / sizeof(flag_sets[0]); ++i) +	{ +		unsigned int flags = flag_sets[i]; + +		CHECK(doc.load(STR("<?pi?><?pi value?>"), flags)); +		CHECK(!doc.first_child()); + +		CHECK(doc.load(STR("<?pi <tag/> value?>"), flags)); +		CHECK(!doc.first_child()); +	} +} + +TEST(parse_pi_parse) +{ +	xml_document doc; +	CHECK(doc.load(STR("<?pi1?><?pi2 value?>"), parse_minimal | parse_pi)); + +	xml_node pi1 = doc.first_child(); +	xml_node pi2 = doc.last_child(); + +	CHECK(pi1 != pi2); +	CHECK(pi1.type() == node_pi); +	CHECK_STRING(pi1.name(), STR("pi1")); +	CHECK_STRING(pi1.value(), STR("")); +	CHECK(pi2.type() == node_pi); +	CHECK_STRING(pi2.name(), STR("pi2")); +	CHECK_STRING(pi2.value(), STR("value")); +} + +TEST(parse_pi_error) +{ +	xml_document doc; + +	unsigned int flag_sets[] = {parse_minimal, parse_minimal | parse_pi}; + +	for (unsigned int i = 0; i < sizeof(flag_sets) / sizeof(flag_sets[0]); ++i) +	{ +		unsigned int flags = flag_sets[i]; + +		CHECK(doc.load(STR("<?"), flags).status == status_bad_pi); +		CHECK(doc.load(STR("<??"), flags).status == status_bad_pi); +		CHECK(doc.load(STR("<?>"), flags).status == status_bad_pi); +		CHECK(doc.load(STR("<?#?>"), flags).status == status_bad_pi); +		CHECK(doc.load(STR("<?name"), flags).status == status_bad_pi); +		CHECK(doc.load(STR("<?name>"), flags).status == status_bad_pi); +		CHECK(doc.load(STR("<?name ?"), flags).status == status_bad_pi); +		CHECK(doc.load(STR("<?name?"), flags).status == status_bad_pi); +		CHECK(doc.load(STR("<?name? "), flags).status == status_bad_pi); +		CHECK(doc.load(STR("<?name?  "), flags).status == status_bad_pi); +		CHECK(doc.load(STR("<?name "), flags).status == status_bad_pi); +		CHECK(doc.load(STR("<?name  "), flags).status == status_bad_pi); +		CHECK(doc.load(STR("<?name   "), flags).status == status_bad_pi); +		CHECK(doc.load(STR("<?name value"), flags).status == status_bad_pi); +		CHECK(doc.load(STR("<?name value "), flags).status == status_bad_pi); +		CHECK(doc.load(STR("<?name value  "), flags).status == status_bad_pi); +		CHECK(doc.load(STR("<?name value  ?"), flags).status == status_bad_pi); +		CHECK(doc.load(STR("<?name value  ? "), flags).status == status_bad_pi); +		CHECK(doc.load(STR("<?name value  ? >"), flags).status == status_bad_pi); +		CHECK(doc.load(STR("<?name value  ? > "), flags).status == status_bad_pi); +		CHECK(doc.load(STR("<?name&"), flags).status == status_bad_pi); +		CHECK(doc.load(STR("<?name&?"), flags).status == status_bad_pi); +	} +	 +	CHECK(doc.load(STR("<?xx#?>"), parse_minimal | parse_pi).status == status_bad_pi); +	CHECK(doc.load(STR("<?name&?>"), parse_minimal | parse_pi).status == status_bad_pi); +	CHECK(doc.load(STR("<?name& x?>"), parse_minimal | parse_pi).status == status_bad_pi); +} + +TEST(parse_comments_skip) +{ +	xml_document doc; +	CHECK(doc.load(STR("<!----><!--value-->"), parse_minimal)); +	CHECK(!doc.first_child()); +} + +TEST(parse_comments_parse) +{ +	xml_document doc; +	CHECK(doc.load(STR("<!----><!--value-->"), parse_minimal | parse_comments)); + +	xml_node c1 = doc.first_child(); +	xml_node c2 = doc.last_child(); + +	CHECK(c1 != c2); +	CHECK(c1.type() == node_comment); +	CHECK_STRING(c1.name(), STR("")); +	CHECK_STRING(c1.value(), STR("")); +	CHECK(c2.type() == node_comment); +	CHECK_STRING(c2.name(), STR("")); +	CHECK_STRING(c2.value(), STR("value")); +} + +TEST(parse_comments_parse_no_eol) +{ +	xml_document doc; +	CHECK(doc.load(STR("<!--\r\rval1\rval2\r\nval3\nval4\r\r-->"), parse_minimal | parse_comments)); + +	xml_node c = doc.first_child(); +	CHECK(c.type() == node_comment); +	CHECK_STRING(c.value(), STR("\r\rval1\rval2\r\nval3\nval4\r\r")); +} + +TEST(parse_comments_parse_eol) +{ +	xml_document doc; +	CHECK(doc.load(STR("<!--\r\rval1\rval2\r\nval3\nval4\r\r-->"), parse_minimal | parse_comments | parse_eol)); + +	xml_node c = doc.first_child(); +	CHECK(c.type() == node_comment); +	CHECK_STRING(c.value(), STR("\n\nval1\nval2\nval3\nval4\n\n")); +} + +TEST(parse_comments_error) +{ +	xml_document doc; + +	unsigned int flag_sets[] = {parse_minimal, parse_minimal | parse_comments, parse_minimal | parse_comments | parse_eol}; + +	for (unsigned int i = 0; i < sizeof(flag_sets) / sizeof(flag_sets[0]); ++i) +	{ +		unsigned int flags = flag_sets[i]; + +		CHECK(doc.load(STR("<!-"), flags).status == status_bad_comment); +		CHECK(doc.load(STR("<!--"), flags).status == status_bad_comment); +		CHECK(doc.load(STR("<!--v"), flags).status == status_bad_comment); +		CHECK(doc.load(STR("<!-->"), flags).status == status_bad_comment); +		CHECK(doc.load(STR("<!--->"), flags).status == status_bad_comment); +		CHECK(doc.load(STR("<!-- <!-- --><!- -->"), flags).status == status_bad_comment); +	} +} + +TEST(parse_cdata_skip) +{ +	xml_document doc; +	CHECK(doc.load(STR("<![CDATA[]]><![CDATA[value]]>"), parse_minimal)); +	CHECK(!doc.first_child()); +} + +TEST(parse_cdata_parse) +{ +	xml_document doc; +	CHECK(doc.load(STR("<![CDATA[]]><![CDATA[value]]>"), parse_minimal | parse_cdata)); + +	xml_node c1 = doc.first_child(); +	xml_node c2 = doc.last_child(); + +	CHECK(c1 != c2); +	CHECK(c1.type() == node_cdata); +	CHECK_STRING(c1.name(), STR("")); +	CHECK_STRING(c1.value(), STR("")); +	CHECK(c2.type() == node_cdata); +	CHECK_STRING(c2.name(), STR("")); +	CHECK_STRING(c2.value(), STR("value")); +} + +TEST(parse_cdata_parse_no_eol) +{ +	xml_document doc; +	CHECK(doc.load(STR("<![CDATA[\r\rval1\rval2\r\nval3\nval4\r\r]]>"), parse_minimal | parse_cdata)); + +	xml_node c = doc.first_child(); +	CHECK(c.type() == node_cdata); +	CHECK_STRING(c.value(), STR("\r\rval1\rval2\r\nval3\nval4\r\r")); +} + +TEST(parse_cdata_parse_eol) +{ +	xml_document doc; +	CHECK(doc.load(STR("<![CDATA[\r\rval1\rval2\r\nval3\nval4\r\r]]>"), parse_minimal | parse_cdata | parse_eol)); + +	xml_node c = doc.first_child(); +	CHECK(c.type() == node_cdata); +	CHECK_STRING(c.value(), STR("\n\nval1\nval2\nval3\nval4\n\n")); +} + +TEST(parse_cdata_error) +{ +	xml_document doc; + +	unsigned int flag_sets[] = {parse_minimal, parse_minimal | parse_cdata, parse_minimal | parse_cdata | parse_eol}; + +	for (unsigned int i = 0; i < sizeof(flag_sets) / sizeof(flag_sets[0]); ++i) +	{ +		unsigned int flags = flag_sets[i]; + +		CHECK(doc.load(STR("<!["), flags).status == status_bad_cdata); +		CHECK(doc.load(STR("<![C"), flags).status == status_bad_cdata); +		CHECK(doc.load(STR("<![CD"), flags).status == status_bad_cdata); +		CHECK(doc.load(STR("<![CDA"), flags).status == status_bad_cdata); +		CHECK(doc.load(STR("<![CDAT"), flags).status == status_bad_cdata); +		CHECK(doc.load(STR("<![CDATA"), flags).status == status_bad_cdata); +		CHECK(doc.load(STR("<![CDATA["), flags).status == status_bad_cdata); +		CHECK(doc.load(STR("<![CDATA[]"), flags).status == status_bad_cdata); +		CHECK(doc.load(STR("<![CDATA[data"), flags).status == status_bad_cdata); +		CHECK(doc.load(STR("<![CDATA[data]"), flags).status == status_bad_cdata); +		CHECK(doc.load(STR("<![CDATA[data]]"), flags).status == status_bad_cdata); +		CHECK(doc.load(STR("<![CDATA[>"), flags).status == status_bad_cdata); +		CHECK(doc.load(STR("<![CDATA[ <![CDATA[]]><![CDATA ]]>"), flags).status == status_bad_cdata); +	} +} + +TEST(parse_ws_pcdata_skip) +{ +	xml_document doc; +	CHECK(doc.load(STR("  "), parse_minimal)); +	CHECK(!doc.first_child()); + +	CHECK(doc.load(STR("<root>  <node>  </node>  </root>"), parse_minimal)); +	 +	xml_node root = doc.child(STR("root")); +	 +	CHECK(root.first_child() == root.last_child()); +	CHECK(!root.first_child().first_child()); +} + +TEST(parse_ws_pcdata_parse) +{ +	xml_document doc; +	CHECK(doc.load(STR("<root>  <node>  </node>  </root>"), parse_minimal | parse_ws_pcdata)); + +	xml_node root = doc.child(STR("root")); + +	xml_node c1 = root.first_child(); +	xml_node c2 = c1.next_sibling(); +	xml_node c3 = c2.next_sibling(); + +	CHECK(c3 == root.last_child()); + +	CHECK(c1.type() == node_pcdata); +	CHECK_STRING(c1.value(), STR("  ")); +	CHECK(c3.type() == node_pcdata); +	CHECK_STRING(c3.value(), STR("  ")); + +	CHECK(c2.first_child() == c2.last_child()); +	CHECK(c2.first_child().type() == node_pcdata); +	CHECK_STRING(c2.first_child().value(), STR("  ")); +} + +TEST(parse_pcdata_no_eol) +{ +	xml_document doc; +	CHECK(doc.load(STR("<root>\r\rval1\rval2\r\nval3\nval4\r\r</root>"), parse_minimal)); + +	CHECK_STRING(doc.child_value(STR("root")), STR("\r\rval1\rval2\r\nval3\nval4\r\r")); +} + +TEST(parse_pcdata_eol) +{ +	xml_document doc; +	CHECK(doc.load(STR("<root>\r\rval1\rval2\r\nval3\nval4\r\r</root>"), parse_minimal | parse_eol)); + +	CHECK_STRING(doc.child_value(STR("root")), STR("\n\nval1\nval2\nval3\nval4\n\n")); +} + +TEST(parse_pcdata_skip_ext) +{ +	xml_document doc; +	CHECK(doc.load(STR("pre<root/>post"), parse_minimal)); +	CHECK(doc.first_child() == doc.last_child()); +	CHECK(doc.first_child().type() == node_element); +} + +TEST(parse_pcdata_error) +{ +	xml_document doc; +	CHECK(doc.load(STR("<root>pcdata"), parse_minimal).status == status_end_element_mismatch); +} + +TEST(parse_escapes_skip) +{ +	xml_document doc; +	CHECK(doc.load(STR("<node id='<>&'"'><>&'"</node>"), parse_minimal)); +	CHECK_STRING(doc.child(STR("node")).attribute(STR("id")).value(), STR("<>&'"")); +} + +TEST(parse_escapes_parse) +{ +	xml_document doc; +	CHECK(doc.load(STR("<node id='<>&'"'><>&'"</node>"), parse_minimal | parse_escapes)); +	CHECK_STRING(doc.child_value(STR("node")), STR("<>&'\"")); +	CHECK_STRING(doc.child(STR("node")).attribute(STR("id")).value(), STR("<>&'\"")); +} + +TEST(parse_escapes_code) +{ +	xml_document doc; +	CHECK(doc.load(STR("<node>  </node>"), parse_minimal | parse_escapes)); +	CHECK_STRING(doc.child_value(STR("node")), STR("\01  ")); +} + +TEST(parse_escapes_code_exhaustive_dec) +{ +	xml_document doc; +	CHECK(doc.load(STR("<node>&#/;	&#:;&#a;&#A;
</node>"), parse_minimal | parse_escapes)); +	CHECK_STRING(doc.child_value(STR("node")), STR("&#/;\x1\x2\x3\x4\x5\x6\x7\x8\x9&#:;&#a;&#A;
")); +} + +TEST(parse_escapes_code_exhaustive_hex) +{ +	xml_document doc; +	CHECK(doc.load(STR("<node>&#x/;	&#x:;&#x@;

&#xG;&#x`;

&#xg;</node>"), parse_minimal | parse_escapes)); +	CHECK_STRING(doc.child_value(STR("node")), STR("&#x/;\x1\x2\x3\x4\x5\x6\x7\x8\x9&#x:;&#x@;\xa\xb\xc\xd\xe\xf&#xG;&#x`;\xa\xb\xc\xd\xe\xf&#xg;")); +} + +TEST(parse_escapes_code_restore) +{ +	xml_document doc; +	CHECK(doc.load(STR("<node>  - - </node>"), parse_minimal | parse_escapes)); +	CHECK_STRING(doc.child_value(STR("node")), STR("  - - ")); +} + +TEST(parse_escapes_char_restore) +{ +	xml_document doc; + +	CHECK(doc.load(STR("<node>&q &qu &quo " </node>"), parse_minimal | parse_escapes)); +	CHECK_STRING(doc.child_value(STR("node")), STR("&q &qu &quo " ")); + +	CHECK(doc.load(STR("<node>&a &ap &apo &apos </node>"), parse_minimal | parse_escapes)); +	CHECK_STRING(doc.child_value(STR("node")), STR("&a &ap &apo &apos ")); + +	CHECK(doc.load(STR("<node>&a &am & </node>"), parse_minimal | parse_escapes)); +	CHECK_STRING(doc.child_value(STR("node")), STR("&a &am & ")); + +	CHECK(doc.load(STR("<node>&l < </node>"), parse_minimal | parse_escapes)); +	CHECK_STRING(doc.child_value(STR("node")), STR("&l < ")); + +	CHECK(doc.load(STR("<node>&g > </node>"), parse_minimal | parse_escapes)); +	CHECK_STRING(doc.child_value(STR("node")), STR("&g > ")); +} + +TEST(parse_escapes_unicode) +{ +	xml_document doc; +	CHECK(doc.load(STR("<node>γγ𤭢</node>"), parse_minimal | parse_escapes)); + +#ifdef PUGIXML_WCHAR_MODE +	const pugi::char_t* v = doc.child_value(STR("node")); + +	unsigned int v2 = v[2]; +	size_t wcharsize = sizeof(wchar_t); + +	CHECK(v[0] == 0x3b3 && v[1] == 0x3b3 && (wcharsize == 2 ? v[2] == 0xd852 && v[3] == 0xdf62 : v2 == 0x24b62)); +#else +	CHECK_STRING(doc.child_value(STR("node")), "\xce\xb3\xce\xb3\xf0\xa4\xad\xa2"); +#endif +} + +TEST(parse_escapes_error) +{ +	xml_document doc; +	CHECK(doc.load(STR("<node>g;&#ab;"</node>"), parse_minimal | parse_escapes)); +	CHECK_STRING(doc.child_value(STR("node")), STR("g;&#ab;"")); + +	CHECK(!doc.load(STR("<node id='"))); +	CHECK(!doc.load(STR("<node id='&g"))); +	CHECK(!doc.load(STR("<node id='>"))); +	CHECK(!doc.load(STR("<node id='&l"))); +	CHECK(!doc.load(STR("<node id='<"))); +	CHECK(!doc.load(STR("<node id='&a"))); +	CHECK(!doc.load(STR("<node id='&"))); +	CHECK(!doc.load(STR("<node id='&apos"))); +} + +TEST(parse_escapes_code_invalid) +{ +	xml_document doc; +	CHECK(doc.load(STR("<node>&#;&#x;&;&#x-;&#-;</node>"), parse_minimal | parse_escapes)); +	CHECK_STRING(doc.child_value(STR("node")), STR("&#;&#x;&;&#x-;&#-;")); +} + +TEST(parse_attribute_spaces) +{ +	xml_document doc; +	CHECK(doc.load(STR("<node id1='v1' id2 ='v2' id3= 'v3' id4 = 'v4' id5 \n\r\t = \r\t\n 'v5' />"), parse_minimal)); +	CHECK_STRING(doc.child(STR("node")).attribute(STR("id1")).value(), STR("v1")); +	CHECK_STRING(doc.child(STR("node")).attribute(STR("id2")).value(), STR("v2")); +	CHECK_STRING(doc.child(STR("node")).attribute(STR("id3")).value(), STR("v3")); +	CHECK_STRING(doc.child(STR("node")).attribute(STR("id4")).value(), STR("v4")); +	CHECK_STRING(doc.child(STR("node")).attribute(STR("id5")).value(), STR("v5")); +} + +TEST(parse_attribute_quot) +{ +	xml_document doc; +	CHECK(doc.load(STR("<node id1='v1' id2=\"v2\"/>"), parse_minimal)); +	CHECK_STRING(doc.child(STR("node")).attribute(STR("id1")).value(), STR("v1")); +	CHECK_STRING(doc.child(STR("node")).attribute(STR("id2")).value(), STR("v2")); +} + +TEST(parse_attribute_no_eol_no_wconv) +{ +	xml_document doc; +	CHECK(doc.load(STR("<node id=' \t\r\rval1  \rval2\r\nval3\nval4\r\r'/>"), parse_minimal)); +	CHECK_STRING(doc.child(STR("node")).attribute(STR("id")).value(), STR(" \t\r\rval1  \rval2\r\nval3\nval4\r\r")); +} + +TEST(parse_attribute_eol_no_wconv) +{ +	xml_document doc; +	CHECK(doc.load(STR("<node id=' \t\r\rval1  \rval2\r\nval3\nval4\r\r'/>"), parse_minimal | parse_eol)); +	CHECK_STRING(doc.child(STR("node")).attribute(STR("id")).value(), STR(" \t\n\nval1  \nval2\nval3\nval4\n\n")); +} + +TEST(parse_attribute_no_eol_wconv) +{ +	xml_document doc; +	CHECK(doc.load(STR("<node id=' \t\r\rval1  \rval2\r\nval3\nval4\r\r'/>"), parse_minimal | parse_wconv_attribute)); +	CHECK_STRING(doc.child(STR("node")).attribute(STR("id")).value(), STR("    val1   val2 val3 val4  ")); +} + +TEST(parse_attribute_eol_wconv) +{ +	xml_document doc; +	CHECK(doc.load(STR("<node id=' \t\r\rval1  \rval2\r\nval3\nval4\r\r'/>"), parse_minimal | parse_eol | parse_wconv_attribute)); +	CHECK_STRING(doc.child(STR("node")).attribute(STR("id")).value(), STR("    val1   val2 val3 val4  ")); +} + +TEST(parse_attribute_wnorm) +{ +	xml_document doc; + +	for (int eol = 0; eol < 2; ++eol) +		for (int wconv = 0; wconv < 2; ++wconv) +		{ +			unsigned int flags = parse_minimal | parse_wnorm_attribute | (eol ? parse_eol : 0) | (wconv ? parse_wconv_attribute : 0); +			CHECK(doc.load(STR("<node id=' \t\r\rval1  \rval2\r\nval3\nval4\r\r'/>"), flags)); +			CHECK_STRING(doc.child(STR("node")).attribute(STR("id")).value(), STR("val1 val2 val3 val4")); +		} +} + +TEST(parse_attribute_variations) +{ +	xml_document doc; + +	for (int wnorm = 0; wnorm < 2; ++wnorm) +		for (int eol = 0; eol < 2; ++eol) +			for (int wconv = 0; wconv < 2; ++wconv) +				for (int escapes = 0; escapes < 2; ++escapes) +				{ +					unsigned int flags = parse_minimal; +					 +					 flags |= (wnorm ? parse_wnorm_attribute : 0); +					 flags |= (eol ? parse_eol : 0); +					 flags |= (wconv ? parse_wconv_attribute : 0); +					 flags |= (escapes ? parse_escapes : 0); + +					CHECK(doc.load(STR("<node id='1'/>"), flags)); +					CHECK_STRING(doc.child(STR("node")).attribute(STR("id")).value(), STR("1")); +				} +} + + +TEST(parse_attribute_error) +{ +	xml_document doc; +	CHECK(doc.load(STR("<node id"), parse_minimal).status == status_bad_attribute); +	CHECK(doc.load(STR("<node id "), parse_minimal).status == status_bad_attribute); +	CHECK(doc.load(STR("<node id  "), parse_minimal).status == status_bad_attribute); +	CHECK(doc.load(STR("<node id   "), parse_minimal).status == status_bad_attribute); +	CHECK(doc.load(STR("<node id/"), parse_minimal).status == status_bad_attribute); +	CHECK(doc.load(STR("<node id/>"), parse_minimal).status == status_bad_attribute); +	CHECK(doc.load(STR("<node id?/>"), parse_minimal).status == status_bad_attribute); +	CHECK(doc.load(STR("<node id=/>"), parse_minimal).status == status_bad_attribute); +	CHECK(doc.load(STR("<node id='/>"), parse_minimal).status == status_bad_attribute); +	CHECK(doc.load(STR("<node id=\"/>"), parse_minimal).status == status_bad_attribute); +	CHECK(doc.load(STR("<node id=\"'/>"), parse_minimal).status == status_bad_attribute); +	CHECK(doc.load(STR("<node id='\"/>"), parse_minimal).status == status_bad_attribute); +	CHECK(doc.load(STR("<node id='\"/>"), parse_minimal).status == status_bad_attribute); +	CHECK(doc.load(STR("<node #/>"), parse_minimal).status == status_bad_start_element); +	CHECK(doc.load(STR("<node#/>"), parse_minimal).status == status_bad_start_element); +	CHECK(doc.load(STR("<node id1='1'id2='2'/>"), parse_minimal).status == status_bad_attribute); +	CHECK(doc.load(STR("<node id&='1'/>"), parse_minimal).status == status_bad_attribute); +	CHECK(doc.load(STR("<node &='1'/>"), parse_minimal).status == status_bad_start_element); +} + +TEST(parse_tag_single) +{ +	xml_document doc; +	CHECK(doc.load(STR("<node/><node /><node\n/>"), parse_minimal)); +	CHECK_NODE(doc, STR("<node /><node /><node />")); +} + +TEST(parse_tag_hierarchy) +{ +	xml_document doc; +	CHECK(doc.load(STR("<node><n1><n2/></n1><n3><n4><n5></n5></n4></n3 \r\n></node>"), parse_minimal)); +	CHECK_NODE(doc, STR("<node><n1><n2 /></n1><n3><n4><n5 /></n4></n3></node>")); +} + +TEST(parse_tag_error) +{ +	xml_document doc; +	CHECK(doc.load(STR("<"), parse_minimal).status == status_unrecognized_tag); +	CHECK(doc.load(STR("<!"), parse_minimal).status == status_unrecognized_tag); +	CHECK(doc.load(STR("<!D"), parse_minimal).status == status_unrecognized_tag); +	CHECK(doc.load(STR("<#"), parse_minimal).status == status_unrecognized_tag); +	CHECK(doc.load(STR("<node#"), parse_minimal).status == status_bad_start_element); +	CHECK(doc.load(STR("<node"), parse_minimal).status == status_bad_start_element); +	CHECK(doc.load(STR("<node/"), parse_minimal).status == status_bad_start_element); +	CHECK(doc.load(STR("<node /"), parse_minimal).status == status_bad_start_element); +	CHECK(doc.load(STR("<node / "), parse_minimal).status == status_bad_start_element); +	CHECK(doc.load(STR("<node / >"), parse_minimal).status == status_bad_start_element); +	CHECK(doc.load(STR("<node/ >"), parse_minimal).status == status_bad_start_element); +	CHECK(doc.load(STR("</ node>"), parse_minimal).status == status_end_element_mismatch); +	CHECK(doc.load(STR("</node"), parse_minimal).status == status_end_element_mismatch); +	CHECK(doc.load(STR("</node "), parse_minimal).status == status_end_element_mismatch); +	CHECK(doc.load(STR("<node></ node>"), parse_minimal).status == status_end_element_mismatch); +	CHECK(doc.load(STR("<node></node"), parse_minimal).status == status_bad_end_element); +	CHECK(doc.load(STR("<node></node "), parse_minimal).status == status_bad_end_element); +	CHECK(doc.load(STR("<node></nodes>"), parse_minimal).status == status_end_element_mismatch); +	CHECK(doc.load(STR("<node>"), parse_minimal).status == status_end_element_mismatch); +	CHECK(doc.load(STR("<node/><"), parse_minimal).status == status_unrecognized_tag); +	CHECK(doc.load(STR("<node attr='value'>"), parse_minimal).status == status_end_element_mismatch); +	CHECK(doc.load(STR("</></node>"), parse_minimal).status == status_end_element_mismatch); +	CHECK(doc.load(STR("</node>"), parse_minimal).status == status_end_element_mismatch); +	CHECK(doc.load(STR("</>"), parse_minimal).status == status_end_element_mismatch); +	CHECK(doc.load(STR("<node></node v>"), parse_minimal).status == status_bad_end_element); +	CHECK(doc.load(STR("<node&/>"), parse_minimal).status == status_bad_start_element); +	CHECK(doc.load(STR("<node& v='1'/>"), parse_minimal).status == status_bad_start_element); +} + +TEST(parse_declaration_cases) +{ +	xml_document doc; +	CHECK(doc.load(STR("<?xml?><?xmL?><?xMl?><?xML?><?Xml?><?XmL?><?XMl?><?XML?>"), parse_minimal | parse_pi)); +	CHECK(!doc.first_child()); +} + +TEST(parse_declaration_attr_cases) +{ +	xml_document doc; +	CHECK(doc.load(STR("<?xml ?><?xmL ?><?xMl ?><?xML ?><?Xml ?><?XmL ?><?XMl ?><?XML ?>"), parse_minimal | parse_pi)); +	CHECK(!doc.first_child()); +} + +TEST(parse_declaration_skip) +{ +	xml_document doc; + +	unsigned int flag_sets[] = {parse_minimal, parse_minimal | parse_pi}; + +	for (unsigned int i = 0; i < sizeof(flag_sets) / sizeof(flag_sets[0]); ++i) +	{ +		unsigned int flags = flag_sets[i]; + +		CHECK(doc.load(STR("<?xml?><?xml version='1.0'?>"), flags)); +		CHECK(!doc.first_child()); + +		CHECK(doc.load(STR("<?xml <tag/> ?>"), flags)); +		CHECK(!doc.first_child()); +	} +} + +TEST(parse_declaration_parse) +{ +	xml_document doc; +	CHECK(doc.load(STR("<?xml?><?xml version='1.0'?>"), parse_minimal | parse_declaration)); + +	xml_node d1 = doc.first_child(); +	xml_node d2 = doc.last_child(); + +	CHECK(d1 != d2); +	CHECK(d1.type() == node_declaration); +	CHECK_STRING(d1.name(), STR("xml")); +	CHECK(d2.type() == node_declaration); +	CHECK_STRING(d2.name(), STR("xml")); +	CHECK_STRING(d2.attribute(STR("version")).value(), STR("1.0")); +} + +TEST(parse_declaration_error) +{ +	xml_document doc; + +	unsigned int flag_sets[] = {parse_minimal, parse_minimal | parse_declaration}; + +	for (unsigned int i = 0; i < sizeof(flag_sets) / sizeof(flag_sets[0]); ++i) +	{ +		unsigned int flags = flag_sets[i]; + +		CHECK(doc.load(STR("<?xml"), flags).status == status_bad_pi); +		CHECK(doc.load(STR("<?xml?"), flags).status == status_bad_pi); +		CHECK(doc.load(STR("<?xml>"), flags).status == status_bad_pi); +		CHECK(doc.load(STR("<?xml version='1>"), flags).status == status_bad_pi); +	} +	 +	CHECK(doc.load(STR("<?xml version='1?>"), parse_minimal | parse_declaration).status == status_bad_attribute); +	CHECK(doc.load(STR("<foo><?xml version='1'?></foo>"), parse_minimal | parse_declaration).status == status_bad_pi); +} + +TEST(parse_empty) +{ +	xml_document doc; +	CHECK(doc.load(STR("")) && !doc.first_child()); +} + +TEST(parse_out_of_memory) +{ +	test_runner::_memory_fail_threshold = 256; + +	xml_document doc; +	CHECK(doc.load(STR("<foo a='1'/>")).status == status_out_of_memory); +	CHECK(!doc.first_child()); +} + +TEST(parse_out_of_memory_halfway) +{ +	unsigned int count = 10000; +	char_t* text = new char_t[count * 4]; + +	for (unsigned int i = 0; i < count; ++i) +	{ +		text[4*i + 0] = '<'; +		text[4*i + 1] = 'n'; +		text[4*i + 2] = '/'; +		text[4*i + 3] = '>'; +	} + +	test_runner::_memory_fail_threshold = 65536; + +	xml_document doc; +	CHECK(doc.load_buffer_inplace(text, count * 4).status == status_out_of_memory); +	CHECK_NODE(doc.first_child(), STR("<n />")); + +	delete[] text; +} + +static bool test_offset(const char_t* contents, unsigned int options, pugi::xml_parse_status status, ptrdiff_t offset) +{ +	xml_document doc; +	xml_parse_result res = doc.load(contents, options); + +	return res.status == status && res.offset == offset; +} + +#define CHECK_OFFSET(contents, options, status, offset) CHECK(test_offset(STR(contents), options, status, offset)) + +TEST(parse_error_offset) +{ +	CHECK_OFFSET("<node/>", parse_default, status_ok, 0); + +	test_runner::_memory_fail_threshold = 1; +	CHECK_OFFSET("<node/>", parse_default, status_out_of_memory, 0); +	test_runner::_memory_fail_threshold = 0; + +	CHECK_OFFSET("<3d/>", parse_default, status_unrecognized_tag, 1); +	CHECK_OFFSET(" <3d/>", parse_default, status_unrecognized_tag, 2); +	CHECK_OFFSET(" <", parse_default, status_unrecognized_tag, 2); + +	CHECK_OFFSET("<?pi", parse_default, status_bad_pi, 3); +	CHECK_OFFSET("<?pi", parse_default | parse_pi, status_bad_pi, 3); +	CHECK_OFFSET("<?xml", parse_default | parse_declaration, status_bad_pi, 4); + +	CHECK_OFFSET("<!----", parse_default, status_bad_comment, 5); +	CHECK_OFFSET("<!----", parse_default | parse_comments, status_bad_comment, 4); + +	CHECK_OFFSET("<![CDA", parse_default, status_bad_cdata, 5); +	CHECK_OFFSET("<![CDATA[non-terminated]]", parse_default, status_bad_cdata, 9); + +	CHECK_OFFSET("<!DOCTYPE doc", parse_default, status_bad_doctype, 12); +	CHECK_OFFSET("<!DOCTYPE greeting [ <!ATTLIST list type    (bullets|ordered|glossary)  \"orde", parse_default, status_bad_doctype, 76); + +	CHECK_OFFSET("<node", parse_default, status_bad_start_element, 4); +	CHECK_OFFSET("<node ", parse_default, status_bad_start_element, 5); +	CHECK_OFFSET("<nod%>", parse_default, status_bad_start_element, 5); + +	CHECK_OFFSET("<node a=2>", parse_default, status_bad_attribute, 8); +	CHECK_OFFSET("<node a='2>", parse_default, status_bad_attribute, 9); + +	CHECK_OFFSET("<n></n $>", parse_default, status_bad_end_element, 7); +	CHECK_OFFSET("<n></n", parse_default, status_bad_end_element, 5); + +	CHECK_OFFSET("<no></na>", parse_default, status_end_element_mismatch, 8); +	CHECK_OFFSET("<no></nod>", parse_default, status_end_element_mismatch, 9); +} diff --git a/tests/test_parse_doctype.cpp b/tests/test_parse_doctype.cpp index 5ab8140..57f38fb 100644 --- a/tests/test_parse_doctype.cpp +++ b/tests/test_parse_doctype.cpp @@ -1,275 +1,275 @@ -#include "common.hpp"
 -
 -#include <string>
 -
 -static bool test_doctype_wf(const std::basic_string<char_t>& decl)
 -{
 -	xml_document doc;
 -
 -	// standalone
 -	if (!doc.load(decl.c_str()) || (bool)doc.first_child()) return false;
 -
 -	// pcdata pre/postfix
 -	if (!doc.load((STR("a") + decl).c_str()) || (bool)doc.first_child()) return false;
 -	if (!doc.load((decl + STR("b")).c_str()) || (bool)doc.first_child()) return false;
 -	if (!doc.load((STR("a") + decl + STR("b")).c_str()) || (bool)doc.first_child()) return false;
 -
 -	// node pre/postfix
 -	if (!doc.load((STR("<nodea/>") + decl).c_str()) || !test_node(doc, STR("<nodea />"), STR(""), format_raw)) return false;
 -	if (!doc.load((decl + STR("<nodeb/>")).c_str()) || !test_node(doc, STR("<nodeb />"), STR(""), format_raw)) return false;
 -	if (!doc.load((STR("<nodea/>") + decl + STR("<nodeb/>")).c_str()) || !test_node(doc, STR("<nodea /><nodeb />"), STR(""), format_raw)) return false;
 -
 -	// wrap in node to check that doctype is parsed fully (does not leave any "pcdata")
 -	if (!doc.load((STR("<node>") + decl + STR("</node>")).c_str()) || !test_node(doc, STR("<node />"), STR(""), format_raw)) return false;
 -
 -	return true;
 -}
 -
 -static bool test_doctype_nwf(const std::basic_string<char_t>& decl)
 -{
 -	xml_document doc;
 -
 -	// standalone
 -	if (doc.load(decl.c_str()).status != status_bad_doctype) return false;
 -
 -	// pcdata postfix
 -	if (doc.load((decl + STR("b")).c_str()).status != status_bad_doctype) return false;
 -
 -	// node postfix
 -	if (doc.load((decl + STR("<nodeb/>")).c_str()).status != status_bad_doctype) return false;
 -
 -	return true;
 -}
 -
 -#define TEST_DOCTYPE_WF(contents) CHECK(test_doctype_wf(STR(contents)))
 -#define TEST_DOCTYPE_NWF(contents) CHECK(test_doctype_nwf(STR(contents)))
 -
 -TEST(parse_doctype_skip)
 -{
 -	TEST_DOCTYPE_WF("<!DOCTYPE doc>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE doc SYSTEM 'foo'>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE doc SYSTEM \"foo\">");
 -	TEST_DOCTYPE_WF("<!DOCTYPE doc PUBLIC \"foo\" 'bar'>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE doc PUBLIC \"foo'\">");
 -	TEST_DOCTYPE_WF("<!DOCTYPE doc SYSTEM 'foo' [<!ELEMENT foo 'ANY'>]>");
 -}
 -
 -TEST(parse_doctype_error)
 -{
 -	TEST_DOCTYPE_NWF("<!DOCTYPE");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE doc");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE doc SYSTEM 'foo");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE doc SYSTEM \"foo");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE doc PUBLIC \"foo\" 'bar");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE doc PUBLIC \"foo'\"");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE doc SYSTEM 'foo' [<!ELEMENT foo 'ANY");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE doc SYSTEM 'foo' [<!ELEMENT foo 'ANY'>");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE doc SYSTEM 'foo' [<!ELEMENT foo 'ANY'>]");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE doc SYSTEM 'foo' [<!ELEMENT foo 'ANY'>] ");
 -}
 -
 -// Examples from W3C recommendations
 -TEST(parse_doctype_w3c_wf)
 -{
 -	TEST_DOCTYPE_WF("<!DOCTYPE greeting SYSTEM \"hello.dtd\">");
 -	TEST_DOCTYPE_WF("<!DOCTYPE greeting [ <!ELEMENT greeting (#PCDATA)> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE greeting [ <!ATTLIST list type    (bullets|ordered|glossary)  \"ordered\"> <!ATTLIST form method  CDATA   #FIXED \"POST\"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE greeting [ <!ENTITY % draft 'INCLUDE' > <!ENTITY % final 'IGNORE' > <![%draft;[ <!ELEMENT book (comments*, title, body, supplements?)> ]]> <![%final;[ <!ELEMENT book (title, body, supplements?)> ]]>]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE greeting [ <!ENTITY open-hatch PUBLIC \"-//Textuality//TEXT Standard open-hatch boilerplate//EN\" \"http://www.textuality.com/boilerplate/OpenHatch.xml\"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE greeting [ <!ENTITY EndAttr \"27'\" > ]>");
 -}
 -
 -TEST(parse_doctype_w3c_nwf)
 -{
 -	TEST_DOCTYPE_NWF("<!DOCTYPE greeting SYSTEM \"hello.dtd>");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE greeting SYSTEM");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE greeting [ <!ELEMENT greeting (#PCDATA)> ]");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE greeting [ <!ELEMENT greeting (#PCDATA)>");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE greeting [ <!ELEMENT greeting (#PCDATA");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE greeting [ ");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE greeting [ <!ATTLIST list type    (bullets|ordered|glossary)  \"ordered\"> ]");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE greeting [ <!ATTLIST list type    (bullets|ordered|glossary)  \"ordered\">");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE greeting [ <!ATTLIST list type    (bullets|ordered|glossary)  \"orde");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE greeting [ <!ATTLIST list type    (bullets|ordered|glossary) ");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE greeting [ <!ENTITY open-hatch PUBLIC \"-//Textuality//TEXT Standard open-hatch boilerplate//EN\" \"http://www.textuality.com/boilerplate/OpenHatch.x");
 -}
 -
 -// Examples from xmlsuite
 -TEST(parse_doctype_xmlconf_eduni_1)
 -{
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!NOTATION gif SYSTEM \"file:///usr/X11R6/bin/xv\"> <!ENTITY declared SYSTEM \"xyzzy\" NDATA gif> <!ATTLIST foo bar ENTITY \"7\"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!NOTATION gif SYSTEM \"file:///usr/X11R6/bin/xv\"> <!ENTITY declared SYSTEM \"xyzzy\" NDATA gif> <!ATTLIST foo bar ENTITY \"undeclared\"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ENTITY % e SYSTEM \"E60.ent\"> %e; ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ENTITY e \"an &unparsed; entity\"> <!NOTATION gif SYSTEM \"file:///usr/X11R6/bin/xv\"> <!ENTITY unparsed SYSTEM \"xyzzy\" NDATA gif> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ATTLIST foo bar CDATA #IMPLIED> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ATTLIST foo xml:lang CDATA #IMPLIED> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ENTITY e SYSTEM \"E38.ent\"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo SYSTEM \"E36.dtd\">");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ELEMENT bar (foo|foo)> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!NOTATION one SYSTEM \"file:///usr/bin/awk\"> <!ATTLIST foo bar NOTATION (one|one) #IMPLIED> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ATTLIST foo bar (one|one) #IMPLIED> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ATTLIST foo xml:lang NMTOKEN #IMPLIED> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ENTITY gt \">\"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ATTLIST foo bar NMTOKENS #IMPLIED> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ENTITY % pe SYSTEM \"subdir1/E18-pe\"> %pe; %intpe; ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo (PCDATA|foo)*> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo (foo*)> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo (foo*)> <!ENTITY space \"&#32;\"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo (foo*)> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo (foo*)> <!ENTITY space \" \"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo EMPTY> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo EMPTY> <!ENTITY empty \"\"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <![INCLUDE[<!ATTLIST foo bar CDATA #IMPLIED>]]> <![IGNORE[some junk]]> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ENTITY % pe \"hello\"> <!-- If forward were expanded when ent was declared, we were get an error, but it is bypassed and not expanded until ent is used in the instance --> <!ENTITY ent \"%pe; ! &forward;\"> <!ENTITY forward \"goodbye\"> ]>");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ENTITY % e \"bar CDATA #IMPLIED>\"> <!ATTLIST foo %e;");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ENTITY % e \"bar CDATA #IMPLIED>\"> <!ATTLIST foo %e; ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ENTITY ent SYSTEM 'E18-ent'> ]>");
 -}
 -
 -TEST(parse_doctype_xmlconf_eduni_2)
 -{
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ENTITY % pe \"<!ENTITY ent1 'text'>\"> %pe; <!ELEMENT foo ANY> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ENTITY ent SYSTEM \"ent\"> <!ELEMENT foo ANY> <!ATTLIST foo a CDATA \"contains &ent; reference\"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ATTLIST foo id ID #IMPLIED> <!ATTLIST foo ref IDREF \"undef\"> <!ATTLIST foo ent ENTITY \"undef\"> <!-- can't test NOTATION attribute, because if it's undeclared then we'll get an error for one of the enumerated values being undeclared. --> <!ENTITY ent SYSTEM \"foo\" NDATA not> <!NOTATION not SYSTEM \"not\"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo EMPTY> <!ATTLIST foo a (one|two|three) \"four\"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ATTLIST foo a NOTATION (not) \"not2\"> <!NOTATION not SYSTEM \"not\"> <!NOTATION not2 SYSTEM \"not2\"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo EMPTY> <!ATTLIST foo a NMTOKENS \"34+\"> ]>");
 -}
 -
 -TEST(parse_doctype_xmlconf_eduni_3)
 -{
 -	TEST_DOCTYPE_WF("<!DOCTYPE animal [ <!ELEMENT animal ANY> <?_\xd9\x9f an only legal per 5th edition extender #x65f in PITarget ?> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT root ANY> <!ELEMENT \xc2\xb7_BadName EMPTY> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE doc [ <!ENTITY e \"<X๜></X๜>\"> ]>");
 -}
 -
 -TEST(parse_doctype_xmlconf_eduni_4)
 -{
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ENTITY a:b \"bogus\"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ATTLIST foo xmlns:a CDATA #IMPLIED xmlns:b NMTOKEN #IMPLIED xmlns:c CDATA #IMPLIED> <!ELEMENT bar ANY> <!ATTLIST bar a:attr CDATA #IMPLIED b:attr CDATA #IMPLIED c:attr CDATA #IMPLIED> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ATTLIST foo xmlns:a CDATA #IMPLIED xmlns:b CDATA #IMPLIED xmlns:c CDATA #IMPLIED> <!ELEMENT bar ANY> <!ATTLIST bar a:attr CDATA #IMPLIED b:attr CDATA #IMPLIED c:attr CDATA #IMPLIED> <!ENTITY tilde \"~\"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT xmlns:foo EMPTY> ]>");
 -}
 -
 -TEST(parse_doctype_xmlconf_eduni_5)
 -{
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ENTITY e \"\"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo (foo*)> <!ENTITY e \"abc…def\"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ATTLIST foo bar  NMTOKENS #IMPLIED> <!ENTITY val \"abc…def\"> ]>");
 -}
 -
 -TEST(parse_doctype_xmlconf_ibm_1)
 -{
 -	TEST_DOCTYPE_WF("<!DOCTYPE animal SYSTEM \"ibm32i04.dtd\" [ <!ATTLIST animal xml:space (default|preserve) 'preserve'> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT root (a,b)> <!ELEMENT a EMPTY> <!ELEMENT b (#PCDATA|c)* > <!ELEMENT c ANY> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT root (PCDATA|b)* > <!ELEMENT b (#PCDATA) > <!ATTLIST b attr1 CDATA #REQUIRED> <!ATTLIST b attr2 (abc|def) \"abc\"> <!ATTLIST b attr3 CDATA #FIXED \"fixed\"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE test [ <!ELEMENT test ANY> <!ELEMENT landscape EMPTY> <!ENTITY parsedentity1 SYSTEM \"ibm56iv01.xml\"> <!ENTITY parsedentity2 SYSTEM \"ibm56iv02.xml\"> <!ATTLIST landscape sun ENTITIES #IMPLIED> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE test [ <!ELEMENT test ANY> <!ELEMENT landscape EMPTY> <!ENTITY image1 SYSTEM \"d:\\testspec\\images\\sunset.gif\" NDATA gif> <!ENTITY image2 SYSTEM \"d:\\testspec\\images\\frontpag.gif\" NDATA gif> <!ATTLIST landscape sun ENTITIES #IMPLIED> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE tokenizer [ <!ELEMENT tokenizer ANY> <!ATTLIST tokenizer UniqueName ID #FIXED \"AC1999\"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE test [ <!ELEMENT test ANY> <!ELEMENT blob (#PCDATA)> <!NOTATION base64 SYSTEM \"mimecode\"> <!NOTATION uuencode SYSTEM \"uudecode\"> <!ATTLIST blob content-encoding NOTATION (base64|uuencode|raw|ascii) #REQUIRED> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE test [ <!ELEMENT test ANY> <!ELEMENT a EMPTY> <!ELEMENT nametoken EMPTY> <!ATTLIST nametoken namevalue NMTOKEN \"@#$\"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT root (#PCDATA)* > <!ENTITY % pe1 SYSTEM \"ibm68i04.ent\"> %pe1; ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT a EMPTY> <!ATTLIST a attr1 CDATA \"&ge1;\"> <!--* GE reference in attr default before declaration *--> <!ENTITY ge1 \"abcdef\"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ENTITY ge1 \"abcdef\"> <!ELEMENT a EMPTY> <!ATTLIST a attr1 CDATA  \"&ge1;\"> <!ENTITY % pe2 \"<!ATTLIST a attr2 CDATA #IMPLIED>\"> %pe3; <!--* PE reference in above doesn't match declaration *--> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT root (#PCDATA)> <!ATTLIST root att CDATA #IMPLIED> <!ENTITY % pe1 '<!ATTLIST root att2 CDATA \"&ge1;\">'> <!ENTITY ge1 \"attdefaultvalue\" > %pe1; <!--* notation JPGformat not declared *--> <!ENTITY ge2  SYSTEM \"image.jpg\" NDATA JPGformat> ]>");
 -}
 -
 -TEST(parse_doctype_xmlconf_ibm_2)
 -{
 -	TEST_DOCTYPE_WF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <!ENTITY gewithElemnetDecl \"<!ELEMENT bogus ANY>\"> <!ATTLIST student att1 CDATA #REQUIRED> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <!ENTITY gewithlt \"abcd&#x3c;\"> <!ATTLIST student att1 CDATA #REQUIRED> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <!ENTITY gewithlt \"abcd<\"> <!ATTLIST student att1 CDATA #REQUIRED> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE 5A_name_starts_with_digit [ <!ELEMENT 5A_name_starts_with_digit EMPTY> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <!ENTITY FullName \"Snow&Man\"> ]>");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <!ENTITY FullName \"Snow\"Man\"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <!ATTLIST student first CDATA #REQUIRED middle CDATA #IMPLIED last CDATA #IMPLIED > <!ENTITY myfirst \"Snow\"> <!ENTITY mymiddle \"I\"> <!ENTITY mylast \"Man\"> ]>");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE student SYSTEM 'student.DTD [ <!ELEMENT student (#PCDATA)> ]>");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <!ENTITY info PUBLIC '..\\info.dtd> ]>");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <!ENTITY info PUBLIC '..\\info'.dtd'> ]>");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <!ENTITY info PUBLIC \"..\\info.dtd> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE student [ <!ENTITY info PUBLIC \"This is a {test} \" \"student.dtd\"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE aniaml [ <!ELEMENT animal ANY> <!ENTITY generalE \"leopard\"> &generalE; <!ENTITY % parameterE \"<!ELEMENT leopard EMPTY>\"> %parameterE; ] animal>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE animal SYSTEM \"ibm28an01.dtd\" [ <!ELEMENT animal (cat|tiger|leopard)+> <!NOTATION animal_class SYSTEM \"ibm29v01.txt\"> <!ELEMENT cat ANY> <!ENTITY forcat \"This is a small cat\"> <!ELEMENT tiger (#PCDATA)> <!ELEMENT small EMPTY> <!ELEMENT big EMPTY> <!ATTLIST tiger color CDATA #REQUIRED> <?sound \"This is a PI\" ?> <!-- This is a comment --> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE animal [ <!ELEMENT animal ANY> <!ENTITY % parameterE \"cat SYSTEM\"> <!NOTATION %parameterE; \"cat.txt\"> ]>");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE animal [ <!ELEMENT animal ANY> <!ENTITY % parameterE \"A music file -->\"> <!-- Parameter reference appears inside a comment in DTD --> <!-- This is %parameterE; ]>");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE animal [ <!ELEMENT animal ANY> <!ENTITY % parameterE \"A music file ?>\"> <?music %parameterE; ]>");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE animal [ <!ELEMENT animal ANY> <!ENTITY % parameterE \"leopard EMPTY>\"> <!ELEMENT %parameterE; ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT root ANY> <!ATTLIST root attr1 CDATA #IMPLIED> <!ATTLIST root attr2 CDATA #IMPLIED> <!ENTITY withlt \"have <lessthan> inside\"> <!ENTITY aIndirect \"&withlt;\"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT root (#PCDATA)> <!--* Mising Name S contentspec in elementdecl *--> <!ELEMENT > ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT root (#PCDATA)> <!ELEMENT a ANY> <!ELEMENT b ANY> <!--* extra separator in seq *--> <!ELEMENT aElement ((a|b),,a)? > ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT root (#PCDATA)> <!ELEMENT a ANY> <!--* Missing white space before Name in AttDef *--> <!ATTLIST a attr1 CDATA \"default\"attr2 ID #required> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE test [ <!ELEMENT test ANY> <!ELEMENT one EMPTY> <!ELEMENT two EMPTY> <!NOTATION this SYSTEM \"alpha\"> <!ATTLIST three attr NOTATION (\"this\") #IMPLIED> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!-- DTD for Production 62--> <![ include [ <!ELEMENT tiger EMPTY> <!ELEMENT animal ANY> ]]> <!--Negative test with pattern1 of P62--> <!--include(Case sensitive)--> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <![[INCLUDE[ <!ELEMENT tiger EMPTY> <!ELEMENT animal ANY> ]]> ]>");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE root [ <?[INCLUDE[ <!ELEMENT tiger EMPTY> <!ELEMENT animal ANY> ]]> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <![[ <!ELEMENT tiger EMPTY> <!ELEMENT animal ANY> ]]> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <![INCLUDE <!ELEMENT tiger EMPTY> <!ELEMENT animal ANY> ]]> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <![ <!ELEMENT tiger EMPTY> <!ELEMENT animal ANY> [INCLUDE ]]> ]>");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE root [ <![ INCLUDE [ <!ELEMENT tiger EMPTY> <!ELEMENT animal ANY> ]>");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE root [ <![INCLUDE[ ]> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT root (#PCDATA)> <!--* PE referenced before declared, against WFC: entity declared --> %paaa; <!ENTITY % paaa \"<!ATTLIST root att CDATA #IMPLIED>\"> <!ENTITY aaa \"aString\"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT root (#PCDATA)> <!ATTLIST root att CDATA #IMPLIED> <!--* missing space  *--> <!ENTITY% paaa \"<!-- comments -->\"> %paaa; ]>");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE root [ <!ELEMENT root (#PCDATA)> <!ATTLIST root att CDATA #IMPLIED> <!--* missing closing bracket  *--> <!ENTITY % paaa \"<!-- comments -->\" %paaa; ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root PUBLIC \"-//W3C//DTD//EN\"\"empty.dtd\" [ <!ELEMENT root (#PCDATA)> <!ATTLIST root att CDATA #IMPLIED> ]>");
 -}
 -
 -TEST(parse_doctype_xmlconf_ibm_3)
 -{
 -	TEST_DOCTYPE_WF("<!DOCTYPE animal [ <!ELEMENT animal (cat|tiger|leopard)+> <!ELEMENT cat EMPTY> <!ELEMENT tiger (#PCDATA)> <!ELEMENT leopard ANY> <!ELEMENT small EMPTY> <!ELEMENT big EMPTY> <!ATTLIST tiger color CDATA #REQUIRED> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE book [ <!ELEMENT book ANY> <!-- This test case covers     legal character ranges plus discrete legal characters for production 02. --> <?NAME target ?> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <!ATTLIST student first CDATA #REQUIRED middle CDATA #IMPLIED last CDATA #REQUIRED > <!ENTITY myfirst \"Snow\"> <!ENTITY mymiddle \"I\"> <!ENTITY mylast 'Man &myfirst; and &myfirst; mymiddle;.'> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE student SYSTEM 'student.dtd'[ ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <!ENTITY unref SYSTEM \"\"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE student PUBLIC \"\" \"student.dtd\"[ ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE student PUBLIC '' 'student.dtd'[ ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE student PUBLIC \"The big ' in it\" \"student.dtd\"[ ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE student PUBLIC 'The latest version' 'student.dtd'[ ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE student PUBLIC \"#x20 #xD #xA abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ -'()+,./:=?;!*#@$_% \" \"student.dtd\"[ ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <!----> ]>");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <!---> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <?pi?> ]>");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <?> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <?MyInstruct AVOID ? BEFORE > IN PI ?> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE animal SYSTEM \"ibm28v02.dtd\" [ <!NOTATION animal_class SYSTEM \"ibm28v02.txt\"> <!ENTITY forcat \"This is a small cat\"> <!ELEMENT tiger (#PCDATA)> <!ENTITY % make_small \"<!ELEMENT small EMPTY>\"> <!ENTITY % make_leopard_element \"<!ELEMENT leopard ANY>\"> <!ENTITY % make_attlist \"<!ATTLIST tiger color CDATA #REQUIRED>\"> %make_leopard_element; <!ELEMENT cat ANY> %make_small; <!ENTITY % make_big \"<!ELEMENT big EMPTY>\"> %make_big; %make_attlist; <?sound \"This is a PI\" ?> <!-- This is a valid test file for p28 --> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE animal [ <![INCLUDE[ <!ENTITY % rootElement \"<!ELEMENT animal ANY>\"> ]]> %rootElement; <!-- Following is a makupdecl --> <!ENTITY % make_tiger_element \"<!ELEMENT tiger EMPTY>\"> %make_tiger_element; <![IGNORE[ <!ELEMENT animal EMPTY> ]]> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT root (a,b)> <!ELEMENT a EMPTY> <!ELEMENT b (#PCDATA|c)* > <!ELEMENT c ANY> <!ENTITY inContent \"<b>General entity reference in element content</b>\"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT a EMPTY> <!ELEMENT b (#PCDATA|c)* > <!ELEMENT c ANY> <!--* PE replace Text have both parentheses *--> <!ENTITY % seq1 \"(a,b,c)\"> <!ELEMENT child1 %seq1; > <!--* Another legal PE replace Text *--> <!ENTITY % seq2 \"a,b\"> <!ELEMENT child2 (%seq2;,c) > ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <![IGNORE[ Everything is ignored within an ignored section, except the sub-section delimiters '<![' and ']]>'.  These must be balanced <!ok ]]> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <![IGNORE[ Everything is ignored within an ignored section, except the sub-section delimiters '<![' and ']]>'.  These must be balanced <![ <!ELEMENT animal EMPTY> ]]> ]]> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <![IGNORE[ begin Everything is ignored within an ignored section, except the sub-section delimiters '<![' and ']]>'.  These must be balanced <![ <!ELEMENT animal EMPTY> ]]> nesting <![ <!ELEMENT tiger (#PCDATA)> ]]> nesting again <![ <!ELEMENT abc ANY> ]]> end ]]> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!DOCTYPE root SYSTEM \"ibm69v01.dtd\" [ <!ELEMENT root (#PCDATA|a)* > <!ENTITY % pe1 \"<!-- comment in PE -->\"> %pe1; ]> ]>");
 -}
 -
 -TEST(parse_doctype_xmlconf_oasis_1)
 -{
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT doc EMPTY> <!ENTITY % ent1 \"\"> <!ENTITY ent2 \"text2\"> <!ENTITY % ent3 \"<!-- <!DOCTYPE <!ELEMENT <? '''"&ent2; %ent1;\"> <!ENTITY % ent4 '\"\"''\"'> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <![INCLUDE[ <!ENTITY % rootel \"<!ELEMENT doc EMPTY>\"> ]]> %rootel; <!ATTLIST doc att CDATA #IMPLIED> <![IGNORE[ <!ELEMENT doc (a)> ]]> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <![INCLUDE[<![INCLUDE[ <![IGNORE[ ignored ]]> <!ELEMENT doc EMPTY> ]]>]]> <![IGNORE[ ignored ]]> <![IGNORE[ <!ELEMENT doc ignored ]]> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <![INCLUDE[ <![ INCLUDE [ <!ELEMENT doc EMPTY> <![IGNORE[asdfasdf]]> ]]>]]> <![INCLUDE[]]> <![INCLUDE[ ]]> <![INCLUDE[ ]]>  ]>");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE root [ <!ELEMENT doc EMPTY> <![IGNORE[<![]]> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT doc EMPTY> <![IGNORE[ <![INCLUDE[ <!ELEMENT doc ]]>]]> <![ IGNORE [ ]]> <![IGNORE[]]> <![IGNORE[ ]]> <![IGNORE[ ]]> ]>");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE root [ <!ELEMENT doc EMPTY> <![IGNORE[ <![ starts must balance ]]> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT doc EMPTY> <![IGNORE[ Everything is ignored within an ignored section, except the sub-section delimiters '<![' and ']]>'.  These must be balanced, but it is no section keyword is required: <![]]> <![DUNNO[ ]]> <![INCLUDE[ asdfasdfasdf <!OK ]]> ] ]> ]] > ]]> <![IGNORE[ < ![ <! [ <![]]>]]> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE doc [ <!ELEMENT doc EMPTY> <!NOTATION not1 SYSTEM \"a%a&b�<!ELEMENT<!--<?</>?>/\''\"> <!NOTATION not2 SYSTEM 'a b\"\"\"'> <!NOTATION not3 SYSTEM \"\"> <!NOTATION not4 SYSTEM ''> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE doc [ <!ELEMENT doc EMPTY> <!NOTATION not1 PUBLIC \"<\"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE doc [ <!ELEMENT doc EMPTY> <!NOTATION not1 PUBLIC \"a b cdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\"> <!NOTATION not2 PUBLIC '0123456789-()+,./:=?;!*#@$_%'> <!NOTATION not3 PUBLIC \"0123456789-()+,.'/:=?;!*#@$_%\"> ]>");
 -	TEST_DOCTYPE_WF("<!--a <!DOCTYPE <?- ]]>-<[ CDATA [ \"- -'- -<doc>--> <!---->"); // not actually a doctype :)
 -	TEST_DOCTYPE_WF("<?xmla <!DOCTYPE <[ CDATA [</doc> &a%b&#c?>"); // not actually a doctype :)
 -	TEST_DOCTYPE_WF("<!DOCTYPE doc SYSTEM \"p31pass1.dtd\" [<!ELEMENT doc EMPTY>]>");
 -}
 -
 -TEST(parse_doctype_xmlconf_xmltest_1)
 -{
 -	TEST_DOCTYPE_NWF("<!DOCTYPE root [ <![ INCLUDE [ <!ELEMENT doc (#PCDATA)> ]> ]>");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE root [ <!ELEMENT doc (#PCDATA)> <![ IGNORE [");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE root [ <!ELEMENT doc (#PCDATA)> <![ IGNORE [ ]>");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE root [ <!ELEMENT doc (#PCDATA)> <![ INCLUDE [");
 -	TEST_DOCTYPE_NWF("<!DOCTYPE root [ <!ELEMENT doc (#PCDATA)> <![ INCLUDE [ ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT doc EMPTY> <!ENTITY % e \"<!--\"> %e; -->");
 -	TEST_DOCTYPE_WF("<!DOCTYPE doc [ <!NOTATION foo PUBLIC \"[\" \"null.ent\"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> <!ATTLIST doc a CDATA #IMPLIED> <!ENTITY e '\"'> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE doc [ <!ENTITY e \"<foo a='&'></foo>\"> ]>");
 -	TEST_DOCTYPE_WF("<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> <!ENTITY e \"<![CDATA[Tim & Michael]]>\"> ]>");
 -}
 +#include "common.hpp" + +#include <string> + +static bool test_doctype_wf(const std::basic_string<char_t>& decl) +{ +	xml_document doc; + +	// standalone +	if (!doc.load(decl.c_str()) || (bool)doc.first_child()) return false; + +	// pcdata pre/postfix +	if (!doc.load((STR("a") + decl).c_str()) || (bool)doc.first_child()) return false; +	if (!doc.load((decl + STR("b")).c_str()) || (bool)doc.first_child()) return false; +	if (!doc.load((STR("a") + decl + STR("b")).c_str()) || (bool)doc.first_child()) return false; + +	// node pre/postfix +	if (!doc.load((STR("<nodea/>") + decl).c_str()) || !test_node(doc, STR("<nodea />"), STR(""), format_raw)) return false; +	if (!doc.load((decl + STR("<nodeb/>")).c_str()) || !test_node(doc, STR("<nodeb />"), STR(""), format_raw)) return false; +	if (!doc.load((STR("<nodea/>") + decl + STR("<nodeb/>")).c_str()) || !test_node(doc, STR("<nodea /><nodeb />"), STR(""), format_raw)) return false; + +	// wrap in node to check that doctype is parsed fully (does not leave any "pcdata") +	if (!doc.load((STR("<node>") + decl + STR("</node>")).c_str()) || !test_node(doc, STR("<node />"), STR(""), format_raw)) return false; + +	return true; +} + +static bool test_doctype_nwf(const std::basic_string<char_t>& decl) +{ +	xml_document doc; + +	// standalone +	if (doc.load(decl.c_str()).status != status_bad_doctype) return false; + +	// pcdata postfix +	if (doc.load((decl + STR("b")).c_str()).status != status_bad_doctype) return false; + +	// node postfix +	if (doc.load((decl + STR("<nodeb/>")).c_str()).status != status_bad_doctype) return false; + +	return true; +} + +#define TEST_DOCTYPE_WF(contents) CHECK(test_doctype_wf(STR(contents))) +#define TEST_DOCTYPE_NWF(contents) CHECK(test_doctype_nwf(STR(contents))) + +TEST(parse_doctype_skip) +{ +	TEST_DOCTYPE_WF("<!DOCTYPE doc>"); +	TEST_DOCTYPE_WF("<!DOCTYPE doc SYSTEM 'foo'>"); +	TEST_DOCTYPE_WF("<!DOCTYPE doc SYSTEM \"foo\">"); +	TEST_DOCTYPE_WF("<!DOCTYPE doc PUBLIC \"foo\" 'bar'>"); +	TEST_DOCTYPE_WF("<!DOCTYPE doc PUBLIC \"foo'\">"); +	TEST_DOCTYPE_WF("<!DOCTYPE doc SYSTEM 'foo' [<!ELEMENT foo 'ANY'>]>"); +} + +TEST(parse_doctype_error) +{ +	TEST_DOCTYPE_NWF("<!DOCTYPE"); +	TEST_DOCTYPE_NWF("<!DOCTYPE doc"); +	TEST_DOCTYPE_NWF("<!DOCTYPE doc SYSTEM 'foo"); +	TEST_DOCTYPE_NWF("<!DOCTYPE doc SYSTEM \"foo"); +	TEST_DOCTYPE_NWF("<!DOCTYPE doc PUBLIC \"foo\" 'bar"); +	TEST_DOCTYPE_NWF("<!DOCTYPE doc PUBLIC \"foo'\""); +	TEST_DOCTYPE_NWF("<!DOCTYPE doc SYSTEM 'foo' [<!ELEMENT foo 'ANY"); +	TEST_DOCTYPE_NWF("<!DOCTYPE doc SYSTEM 'foo' [<!ELEMENT foo 'ANY'>"); +	TEST_DOCTYPE_NWF("<!DOCTYPE doc SYSTEM 'foo' [<!ELEMENT foo 'ANY'>]"); +	TEST_DOCTYPE_NWF("<!DOCTYPE doc SYSTEM 'foo' [<!ELEMENT foo 'ANY'>] "); +} + +// Examples from W3C recommendations +TEST(parse_doctype_w3c_wf) +{ +	TEST_DOCTYPE_WF("<!DOCTYPE greeting SYSTEM \"hello.dtd\">"); +	TEST_DOCTYPE_WF("<!DOCTYPE greeting [ <!ELEMENT greeting (#PCDATA)> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE greeting [ <!ATTLIST list type    (bullets|ordered|glossary)  \"ordered\"> <!ATTLIST form method  CDATA   #FIXED \"POST\"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE greeting [ <!ENTITY % draft 'INCLUDE' > <!ENTITY % final 'IGNORE' > <![%draft;[ <!ELEMENT book (comments*, title, body, supplements?)> ]]> <![%final;[ <!ELEMENT book (title, body, supplements?)> ]]>]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE greeting [ <!ENTITY open-hatch PUBLIC \"-//Textuality//TEXT Standard open-hatch boilerplate//EN\" \"http://www.textuality.com/boilerplate/OpenHatch.xml\"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE greeting [ <!ENTITY EndAttr \"27'\" > ]>"); +} + +TEST(parse_doctype_w3c_nwf) +{ +	TEST_DOCTYPE_NWF("<!DOCTYPE greeting SYSTEM \"hello.dtd>"); +	TEST_DOCTYPE_NWF("<!DOCTYPE greeting SYSTEM"); +	TEST_DOCTYPE_NWF("<!DOCTYPE greeting [ <!ELEMENT greeting (#PCDATA)> ]"); +	TEST_DOCTYPE_NWF("<!DOCTYPE greeting [ <!ELEMENT greeting (#PCDATA)>"); +	TEST_DOCTYPE_NWF("<!DOCTYPE greeting [ <!ELEMENT greeting (#PCDATA"); +	TEST_DOCTYPE_NWF("<!DOCTYPE greeting [ "); +	TEST_DOCTYPE_NWF("<!DOCTYPE greeting [ <!ATTLIST list type    (bullets|ordered|glossary)  \"ordered\"> ]"); +	TEST_DOCTYPE_NWF("<!DOCTYPE greeting [ <!ATTLIST list type    (bullets|ordered|glossary)  \"ordered\">"); +	TEST_DOCTYPE_NWF("<!DOCTYPE greeting [ <!ATTLIST list type    (bullets|ordered|glossary)  \"orde"); +	TEST_DOCTYPE_NWF("<!DOCTYPE greeting [ <!ATTLIST list type    (bullets|ordered|glossary) "); +	TEST_DOCTYPE_NWF("<!DOCTYPE greeting [ <!ENTITY open-hatch PUBLIC \"-//Textuality//TEXT Standard open-hatch boilerplate//EN\" \"http://www.textuality.com/boilerplate/OpenHatch.x"); +} + +// Examples from xmlsuite +TEST(parse_doctype_xmlconf_eduni_1) +{ +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!NOTATION gif SYSTEM \"file:///usr/X11R6/bin/xv\"> <!ENTITY declared SYSTEM \"xyzzy\" NDATA gif> <!ATTLIST foo bar ENTITY \"7\"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!NOTATION gif SYSTEM \"file:///usr/X11R6/bin/xv\"> <!ENTITY declared SYSTEM \"xyzzy\" NDATA gif> <!ATTLIST foo bar ENTITY \"undeclared\"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ENTITY % e SYSTEM \"E60.ent\"> %e; ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ENTITY e \"an &unparsed; entity\"> <!NOTATION gif SYSTEM \"file:///usr/X11R6/bin/xv\"> <!ENTITY unparsed SYSTEM \"xyzzy\" NDATA gif> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ATTLIST foo bar CDATA #IMPLIED> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ATTLIST foo xml:lang CDATA #IMPLIED> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ENTITY e SYSTEM \"E38.ent\"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo SYSTEM \"E36.dtd\">"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ELEMENT bar (foo|foo)> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!NOTATION one SYSTEM \"file:///usr/bin/awk\"> <!ATTLIST foo bar NOTATION (one|one) #IMPLIED> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ATTLIST foo bar (one|one) #IMPLIED> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ATTLIST foo xml:lang NMTOKEN #IMPLIED> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ENTITY gt \">\"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ATTLIST foo bar NMTOKENS #IMPLIED> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ENTITY % pe SYSTEM \"subdir1/E18-pe\"> %pe; %intpe; ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo (PCDATA|foo)*> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo (foo*)> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo (foo*)> <!ENTITY space \"&#32;\"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo (foo*)> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo (foo*)> <!ENTITY space \" \"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo EMPTY> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo EMPTY> <!ENTITY empty \"\"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <![INCLUDE[<!ATTLIST foo bar CDATA #IMPLIED>]]> <![IGNORE[some junk]]> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ENTITY % pe \"hello\"> <!-- If forward were expanded when ent was declared, we were get an error, but it is bypassed and not expanded until ent is used in the instance --> <!ENTITY ent \"%pe; ! &forward;\"> <!ENTITY forward \"goodbye\"> ]>"); +	TEST_DOCTYPE_NWF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ENTITY % e \"bar CDATA #IMPLIED>\"> <!ATTLIST foo %e;"); +	TEST_DOCTYPE_NWF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ENTITY % e \"bar CDATA #IMPLIED>\"> <!ATTLIST foo %e; ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ENTITY ent SYSTEM 'E18-ent'> ]>"); +} + +TEST(parse_doctype_xmlconf_eduni_2) +{ +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ENTITY % pe \"<!ENTITY ent1 'text'>\"> %pe; <!ELEMENT foo ANY> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ENTITY ent SYSTEM \"ent\"> <!ELEMENT foo ANY> <!ATTLIST foo a CDATA \"contains &ent; reference\"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ATTLIST foo id ID #IMPLIED> <!ATTLIST foo ref IDREF \"undef\"> <!ATTLIST foo ent ENTITY \"undef\"> <!-- can't test NOTATION attribute, because if it's undeclared then we'll get an error for one of the enumerated values being undeclared. --> <!ENTITY ent SYSTEM \"foo\" NDATA not> <!NOTATION not SYSTEM \"not\"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo EMPTY> <!ATTLIST foo a (one|two|three) \"four\"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ATTLIST foo a NOTATION (not) \"not2\"> <!NOTATION not SYSTEM \"not\"> <!NOTATION not2 SYSTEM \"not2\"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo EMPTY> <!ATTLIST foo a NMTOKENS \"34+\"> ]>"); +} + +TEST(parse_doctype_xmlconf_eduni_3) +{ +	TEST_DOCTYPE_WF("<!DOCTYPE animal [ <!ELEMENT animal ANY> <?_\xd9\x9f an only legal per 5th edition extender #x65f in PITarget ?> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT root ANY> <!ELEMENT \xc2\xb7_BadName EMPTY> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE doc [ <!ENTITY e \"<X๜></X๜>\"> ]>"); +} + +TEST(parse_doctype_xmlconf_eduni_4) +{ +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ENTITY a:b \"bogus\"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ATTLIST foo xmlns:a CDATA #IMPLIED xmlns:b NMTOKEN #IMPLIED xmlns:c CDATA #IMPLIED> <!ELEMENT bar ANY> <!ATTLIST bar a:attr CDATA #IMPLIED b:attr CDATA #IMPLIED c:attr CDATA #IMPLIED> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ATTLIST foo xmlns:a CDATA #IMPLIED xmlns:b CDATA #IMPLIED xmlns:c CDATA #IMPLIED> <!ELEMENT bar ANY> <!ATTLIST bar a:attr CDATA #IMPLIED b:attr CDATA #IMPLIED c:attr CDATA #IMPLIED> <!ENTITY tilde \"~\"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT xmlns:foo EMPTY> ]>"); +} + +TEST(parse_doctype_xmlconf_eduni_5) +{ +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ENTITY e \"\"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo (foo*)> <!ENTITY e \"abc…def\"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE foo [ <!ELEMENT foo ANY> <!ATTLIST foo bar  NMTOKENS #IMPLIED> <!ENTITY val \"abc…def\"> ]>"); +} + +TEST(parse_doctype_xmlconf_ibm_1) +{ +	TEST_DOCTYPE_WF("<!DOCTYPE animal SYSTEM \"ibm32i04.dtd\" [ <!ATTLIST animal xml:space (default|preserve) 'preserve'> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT root (a,b)> <!ELEMENT a EMPTY> <!ELEMENT b (#PCDATA|c)* > <!ELEMENT c ANY> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT root (PCDATA|b)* > <!ELEMENT b (#PCDATA) > <!ATTLIST b attr1 CDATA #REQUIRED> <!ATTLIST b attr2 (abc|def) \"abc\"> <!ATTLIST b attr3 CDATA #FIXED \"fixed\"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE test [ <!ELEMENT test ANY> <!ELEMENT landscape EMPTY> <!ENTITY parsedentity1 SYSTEM \"ibm56iv01.xml\"> <!ENTITY parsedentity2 SYSTEM \"ibm56iv02.xml\"> <!ATTLIST landscape sun ENTITIES #IMPLIED> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE test [ <!ELEMENT test ANY> <!ELEMENT landscape EMPTY> <!ENTITY image1 SYSTEM \"d:\\testspec\\images\\sunset.gif\" NDATA gif> <!ENTITY image2 SYSTEM \"d:\\testspec\\images\\frontpag.gif\" NDATA gif> <!ATTLIST landscape sun ENTITIES #IMPLIED> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE tokenizer [ <!ELEMENT tokenizer ANY> <!ATTLIST tokenizer UniqueName ID #FIXED \"AC1999\"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE test [ <!ELEMENT test ANY> <!ELEMENT blob (#PCDATA)> <!NOTATION base64 SYSTEM \"mimecode\"> <!NOTATION uuencode SYSTEM \"uudecode\"> <!ATTLIST blob content-encoding NOTATION (base64|uuencode|raw|ascii) #REQUIRED> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE test [ <!ELEMENT test ANY> <!ELEMENT a EMPTY> <!ELEMENT nametoken EMPTY> <!ATTLIST nametoken namevalue NMTOKEN \"@#$\"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT root (#PCDATA)* > <!ENTITY % pe1 SYSTEM \"ibm68i04.ent\"> %pe1; ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT a EMPTY> <!ATTLIST a attr1 CDATA \"&ge1;\"> <!--* GE reference in attr default before declaration *--> <!ENTITY ge1 \"abcdef\"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ENTITY ge1 \"abcdef\"> <!ELEMENT a EMPTY> <!ATTLIST a attr1 CDATA  \"&ge1;\"> <!ENTITY % pe2 \"<!ATTLIST a attr2 CDATA #IMPLIED>\"> %pe3; <!--* PE reference in above doesn't match declaration *--> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT root (#PCDATA)> <!ATTLIST root att CDATA #IMPLIED> <!ENTITY % pe1 '<!ATTLIST root att2 CDATA \"&ge1;\">'> <!ENTITY ge1 \"attdefaultvalue\" > %pe1; <!--* notation JPGformat not declared *--> <!ENTITY ge2  SYSTEM \"image.jpg\" NDATA JPGformat> ]>"); +} + +TEST(parse_doctype_xmlconf_ibm_2) +{ +	TEST_DOCTYPE_WF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <!ENTITY gewithElemnetDecl \"<!ELEMENT bogus ANY>\"> <!ATTLIST student att1 CDATA #REQUIRED> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <!ENTITY gewithlt \"abcd&#x3c;\"> <!ATTLIST student att1 CDATA #REQUIRED> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <!ENTITY gewithlt \"abcd<\"> <!ATTLIST student att1 CDATA #REQUIRED> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE 5A_name_starts_with_digit [ <!ELEMENT 5A_name_starts_with_digit EMPTY> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <!ENTITY FullName \"Snow&Man\"> ]>"); +	TEST_DOCTYPE_NWF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <!ENTITY FullName \"Snow\"Man\"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <!ATTLIST student first CDATA #REQUIRED middle CDATA #IMPLIED last CDATA #IMPLIED > <!ENTITY myfirst \"Snow\"> <!ENTITY mymiddle \"I\"> <!ENTITY mylast \"Man\"> ]>"); +	TEST_DOCTYPE_NWF("<!DOCTYPE student SYSTEM 'student.DTD [ <!ELEMENT student (#PCDATA)> ]>"); +	TEST_DOCTYPE_NWF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <!ENTITY info PUBLIC '..\\info.dtd> ]>"); +	TEST_DOCTYPE_NWF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <!ENTITY info PUBLIC '..\\info'.dtd'> ]>"); +	TEST_DOCTYPE_NWF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <!ENTITY info PUBLIC \"..\\info.dtd> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE student [ <!ENTITY info PUBLIC \"This is a {test} \" \"student.dtd\"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE aniaml [ <!ELEMENT animal ANY> <!ENTITY generalE \"leopard\"> &generalE; <!ENTITY % parameterE \"<!ELEMENT leopard EMPTY>\"> %parameterE; ] animal>"); +	TEST_DOCTYPE_WF("<!DOCTYPE animal SYSTEM \"ibm28an01.dtd\" [ <!ELEMENT animal (cat|tiger|leopard)+> <!NOTATION animal_class SYSTEM \"ibm29v01.txt\"> <!ELEMENT cat ANY> <!ENTITY forcat \"This is a small cat\"> <!ELEMENT tiger (#PCDATA)> <!ELEMENT small EMPTY> <!ELEMENT big EMPTY> <!ATTLIST tiger color CDATA #REQUIRED> <?sound \"This is a PI\" ?> <!-- This is a comment --> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE animal [ <!ELEMENT animal ANY> <!ENTITY % parameterE \"cat SYSTEM\"> <!NOTATION %parameterE; \"cat.txt\"> ]>"); +	TEST_DOCTYPE_NWF("<!DOCTYPE animal [ <!ELEMENT animal ANY> <!ENTITY % parameterE \"A music file -->\"> <!-- Parameter reference appears inside a comment in DTD --> <!-- This is %parameterE; ]>"); +	TEST_DOCTYPE_NWF("<!DOCTYPE animal [ <!ELEMENT animal ANY> <!ENTITY % parameterE \"A music file ?>\"> <?music %parameterE; ]>"); +	TEST_DOCTYPE_NWF("<!DOCTYPE animal [ <!ELEMENT animal ANY> <!ENTITY % parameterE \"leopard EMPTY>\"> <!ELEMENT %parameterE; ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT root ANY> <!ATTLIST root attr1 CDATA #IMPLIED> <!ATTLIST root attr2 CDATA #IMPLIED> <!ENTITY withlt \"have <lessthan> inside\"> <!ENTITY aIndirect \"&withlt;\"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT root (#PCDATA)> <!--* Mising Name S contentspec in elementdecl *--> <!ELEMENT > ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT root (#PCDATA)> <!ELEMENT a ANY> <!ELEMENT b ANY> <!--* extra separator in seq *--> <!ELEMENT aElement ((a|b),,a)? > ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT root (#PCDATA)> <!ELEMENT a ANY> <!--* Missing white space before Name in AttDef *--> <!ATTLIST a attr1 CDATA \"default\"attr2 ID #required> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE test [ <!ELEMENT test ANY> <!ELEMENT one EMPTY> <!ELEMENT two EMPTY> <!NOTATION this SYSTEM \"alpha\"> <!ATTLIST three attr NOTATION (\"this\") #IMPLIED> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!-- DTD for Production 62--> <![ include [ <!ELEMENT tiger EMPTY> <!ELEMENT animal ANY> ]]> <!--Negative test with pattern1 of P62--> <!--include(Case sensitive)--> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <![[INCLUDE[ <!ELEMENT tiger EMPTY> <!ELEMENT animal ANY> ]]> ]>"); +	TEST_DOCTYPE_NWF("<!DOCTYPE root [ <?[INCLUDE[ <!ELEMENT tiger EMPTY> <!ELEMENT animal ANY> ]]> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <![[ <!ELEMENT tiger EMPTY> <!ELEMENT animal ANY> ]]> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <![INCLUDE <!ELEMENT tiger EMPTY> <!ELEMENT animal ANY> ]]> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <![ <!ELEMENT tiger EMPTY> <!ELEMENT animal ANY> [INCLUDE ]]> ]>"); +	TEST_DOCTYPE_NWF("<!DOCTYPE root [ <![ INCLUDE [ <!ELEMENT tiger EMPTY> <!ELEMENT animal ANY> ]>"); +	TEST_DOCTYPE_NWF("<!DOCTYPE root [ <![INCLUDE[ ]> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT root (#PCDATA)> <!--* PE referenced before declared, against WFC: entity declared --> %paaa; <!ENTITY % paaa \"<!ATTLIST root att CDATA #IMPLIED>\"> <!ENTITY aaa \"aString\"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT root (#PCDATA)> <!ATTLIST root att CDATA #IMPLIED> <!--* missing space  *--> <!ENTITY% paaa \"<!-- comments -->\"> %paaa; ]>"); +	TEST_DOCTYPE_NWF("<!DOCTYPE root [ <!ELEMENT root (#PCDATA)> <!ATTLIST root att CDATA #IMPLIED> <!--* missing closing bracket  *--> <!ENTITY % paaa \"<!-- comments -->\" %paaa; ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root PUBLIC \"-//W3C//DTD//EN\"\"empty.dtd\" [ <!ELEMENT root (#PCDATA)> <!ATTLIST root att CDATA #IMPLIED> ]>"); +} + +TEST(parse_doctype_xmlconf_ibm_3) +{ +	TEST_DOCTYPE_WF("<!DOCTYPE animal [ <!ELEMENT animal (cat|tiger|leopard)+> <!ELEMENT cat EMPTY> <!ELEMENT tiger (#PCDATA)> <!ELEMENT leopard ANY> <!ELEMENT small EMPTY> <!ELEMENT big EMPTY> <!ATTLIST tiger color CDATA #REQUIRED> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE book [ <!ELEMENT book ANY> <!-- This test case covers     legal character ranges plus discrete legal characters for production 02. --> <?NAME target ?> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <!ATTLIST student first CDATA #REQUIRED middle CDATA #IMPLIED last CDATA #REQUIRED > <!ENTITY myfirst \"Snow\"> <!ENTITY mymiddle \"I\"> <!ENTITY mylast 'Man &myfirst; and &myfirst; mymiddle;.'> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE student SYSTEM 'student.dtd'[ ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <!ENTITY unref SYSTEM \"\"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE student PUBLIC \"\" \"student.dtd\"[ ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE student PUBLIC '' 'student.dtd'[ ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE student PUBLIC \"The big ' in it\" \"student.dtd\"[ ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE student PUBLIC 'The latest version' 'student.dtd'[ ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE student PUBLIC \"#x20 #xD #xA abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ -'()+,./:=?;!*#@$_% \" \"student.dtd\"[ ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <!----> ]>"); +	TEST_DOCTYPE_NWF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <!---> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <?pi?> ]>"); +	TEST_DOCTYPE_NWF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <?> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE student [ <!ELEMENT student (#PCDATA)> <?MyInstruct AVOID ? BEFORE > IN PI ?> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE animal SYSTEM \"ibm28v02.dtd\" [ <!NOTATION animal_class SYSTEM \"ibm28v02.txt\"> <!ENTITY forcat \"This is a small cat\"> <!ELEMENT tiger (#PCDATA)> <!ENTITY % make_small \"<!ELEMENT small EMPTY>\"> <!ENTITY % make_leopard_element \"<!ELEMENT leopard ANY>\"> <!ENTITY % make_attlist \"<!ATTLIST tiger color CDATA #REQUIRED>\"> %make_leopard_element; <!ELEMENT cat ANY> %make_small; <!ENTITY % make_big \"<!ELEMENT big EMPTY>\"> %make_big; %make_attlist; <?sound \"This is a PI\" ?> <!-- This is a valid test file for p28 --> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE animal [ <![INCLUDE[ <!ENTITY % rootElement \"<!ELEMENT animal ANY>\"> ]]> %rootElement; <!-- Following is a makupdecl --> <!ENTITY % make_tiger_element \"<!ELEMENT tiger EMPTY>\"> %make_tiger_element; <![IGNORE[ <!ELEMENT animal EMPTY> ]]> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT root (a,b)> <!ELEMENT a EMPTY> <!ELEMENT b (#PCDATA|c)* > <!ELEMENT c ANY> <!ENTITY inContent \"<b>General entity reference in element content</b>\"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT a EMPTY> <!ELEMENT b (#PCDATA|c)* > <!ELEMENT c ANY> <!--* PE replace Text have both parentheses *--> <!ENTITY % seq1 \"(a,b,c)\"> <!ELEMENT child1 %seq1; > <!--* Another legal PE replace Text *--> <!ENTITY % seq2 \"a,b\"> <!ELEMENT child2 (%seq2;,c) > ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <![IGNORE[ Everything is ignored within an ignored section, except the sub-section delimiters '<![' and ']]>'.  These must be balanced <!ok ]]> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <![IGNORE[ Everything is ignored within an ignored section, except the sub-section delimiters '<![' and ']]>'.  These must be balanced <![ <!ELEMENT animal EMPTY> ]]> ]]> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <![IGNORE[ begin Everything is ignored within an ignored section, except the sub-section delimiters '<![' and ']]>'.  These must be balanced <![ <!ELEMENT animal EMPTY> ]]> nesting <![ <!ELEMENT tiger (#PCDATA)> ]]> nesting again <![ <!ELEMENT abc ANY> ]]> end ]]> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!DOCTYPE root SYSTEM \"ibm69v01.dtd\" [ <!ELEMENT root (#PCDATA|a)* > <!ENTITY % pe1 \"<!-- comment in PE -->\"> %pe1; ]> ]>"); +} + +TEST(parse_doctype_xmlconf_oasis_1) +{ +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT doc EMPTY> <!ENTITY % ent1 \"\"> <!ENTITY ent2 \"text2\"> <!ENTITY % ent3 \"<!-- <!DOCTYPE <!ELEMENT <? '''"&ent2; %ent1;\"> <!ENTITY % ent4 '\"\"''\"'> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <![INCLUDE[ <!ENTITY % rootel \"<!ELEMENT doc EMPTY>\"> ]]> %rootel; <!ATTLIST doc att CDATA #IMPLIED> <![IGNORE[ <!ELEMENT doc (a)> ]]> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <![INCLUDE[<![INCLUDE[ <![IGNORE[ ignored ]]> <!ELEMENT doc EMPTY> ]]>]]> <![IGNORE[ ignored ]]> <![IGNORE[ <!ELEMENT doc ignored ]]> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <![INCLUDE[ <![ INCLUDE [ <!ELEMENT doc EMPTY> <![IGNORE[asdfasdf]]> ]]>]]> <![INCLUDE[]]> <![INCLUDE[ ]]> <![INCLUDE[ ]]>  ]>"); +	TEST_DOCTYPE_NWF("<!DOCTYPE root [ <!ELEMENT doc EMPTY> <![IGNORE[<![]]> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT doc EMPTY> <![IGNORE[ <![INCLUDE[ <!ELEMENT doc ]]>]]> <![ IGNORE [ ]]> <![IGNORE[]]> <![IGNORE[ ]]> <![IGNORE[ ]]> ]>"); +	TEST_DOCTYPE_NWF("<!DOCTYPE root [ <!ELEMENT doc EMPTY> <![IGNORE[ <![ starts must balance ]]> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT doc EMPTY> <![IGNORE[ Everything is ignored within an ignored section, except the sub-section delimiters '<![' and ']]>'.  These must be balanced, but it is no section keyword is required: <![]]> <![DUNNO[ ]]> <![INCLUDE[ asdfasdfasdf <!OK ]]> ] ]> ]] > ]]> <![IGNORE[ < ![ <! [ <![]]>]]> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE doc [ <!ELEMENT doc EMPTY> <!NOTATION not1 SYSTEM \"a%a&b�<!ELEMENT<!--<?</>?>/\''\"> <!NOTATION not2 SYSTEM 'a b\"\"\"'> <!NOTATION not3 SYSTEM \"\"> <!NOTATION not4 SYSTEM ''> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE doc [ <!ELEMENT doc EMPTY> <!NOTATION not1 PUBLIC \"<\"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE doc [ <!ELEMENT doc EMPTY> <!NOTATION not1 PUBLIC \"a b cdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\"> <!NOTATION not2 PUBLIC '0123456789-()+,./:=?;!*#@$_%'> <!NOTATION not3 PUBLIC \"0123456789-()+,.'/:=?;!*#@$_%\"> ]>"); +	TEST_DOCTYPE_WF("<!--a <!DOCTYPE <?- ]]>-<[ CDATA [ \"- -'- -<doc>--> <!---->"); // not actually a doctype :) +	TEST_DOCTYPE_WF("<?xmla <!DOCTYPE <[ CDATA [</doc> &a%b&#c?>"); // not actually a doctype :) +	TEST_DOCTYPE_WF("<!DOCTYPE doc SYSTEM \"p31pass1.dtd\" [<!ELEMENT doc EMPTY>]>"); +} + +TEST(parse_doctype_xmlconf_xmltest_1) +{ +	TEST_DOCTYPE_NWF("<!DOCTYPE root [ <![ INCLUDE [ <!ELEMENT doc (#PCDATA)> ]> ]>"); +	TEST_DOCTYPE_NWF("<!DOCTYPE root [ <!ELEMENT doc (#PCDATA)> <![ IGNORE ["); +	TEST_DOCTYPE_NWF("<!DOCTYPE root [ <!ELEMENT doc (#PCDATA)> <![ IGNORE [ ]>"); +	TEST_DOCTYPE_NWF("<!DOCTYPE root [ <!ELEMENT doc (#PCDATA)> <![ INCLUDE ["); +	TEST_DOCTYPE_NWF("<!DOCTYPE root [ <!ELEMENT doc (#PCDATA)> <![ INCLUDE [ ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE root [ <!ELEMENT doc EMPTY> <!ENTITY % e \"<!--\"> %e; -->"); +	TEST_DOCTYPE_WF("<!DOCTYPE doc [ <!NOTATION foo PUBLIC \"[\" \"null.ent\"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> <!ATTLIST doc a CDATA #IMPLIED> <!ENTITY e '\"'> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE doc [ <!ENTITY e \"<foo a='&'></foo>\"> ]>"); +	TEST_DOCTYPE_WF("<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> <!ENTITY e \"<![CDATA[Tim & Michael]]>\"> ]>"); +} diff --git a/tests/test_unicode.cpp b/tests/test_unicode.cpp index ea2494b..0b656a3 100644 --- a/tests/test_unicode.cpp +++ b/tests/test_unicode.cpp @@ -1,137 +1,137 @@ -#ifndef PUGIXML_NO_STL
 -
 -#include "common.hpp"
 -
 -#include <string>
 -
 -// letters taken from http://www.utf8-chartable.de/
 -
 -TEST(as_wide_empty)
 -{
 -	CHECK(as_wide("") == L"");
 -}
 -
 -TEST(as_wide_valid_basic)
 -{
 -	// valid 1-byte, 2-byte and 3-byte inputs
 -#ifdef U_LITERALS
 -	CHECK(as_wide("?\xd0\x80\xe2\x80\xbd") == L"?\u0400\u203D");
 -#else
 -	CHECK(as_wide("?\xd0\x80\xe2\x80\xbd") == L"?\x0400\x203D");
 -#endif
 -}
 -
 -TEST(as_wide_valid_astral)
 -{
 -	// valid 4-byte input
 -	std::wstring b4 = as_wide("\xf2\x97\x98\xa4 \xf4\x80\x8f\xbf");
 -
 -	size_t wcharsize = sizeof(wchar_t);
 -
 -	if (wcharsize == 4)
 -	{
 -		CHECK(b4.size() == 3 && b4[0] == wchar_cast(0x97624) && b4[1] == L' ' && b4[2] == wchar_cast(0x1003ff));
 -	}
 -	else
 -	{
 -		CHECK(b4.size() == 5 && b4[0] == 0xda1d && b4[1] == 0xde24 && b4[2] == L' ' && b4[3] == 0xdbc0 && b4[4] == 0xdfff);
 -	}
 -}
 -
 -TEST(as_wide_invalid)
 -{
 -	// invalid 1-byte input
 -	CHECK(as_wide("a\xb0") == L"a");
 -	CHECK(as_wide("a\xb0_") == L"a_");
 -
 -	// invalid 2-byte input
 -	CHECK(as_wide("a\xc0") == L"a");
 -	CHECK(as_wide("a\xd0") == L"a");
 -	CHECK(as_wide("a\xc0_") == L"a_");
 -	CHECK(as_wide("a\xd0_") == L"a_");
 -
 -	// invalid 3-byte input
 -	CHECK(as_wide("a\xe2\x80") == L"a");
 -	CHECK(as_wide("a\xe2") == L"a");
 -	CHECK(as_wide("a\xe2\x80_") == L"a_");
 -	CHECK(as_wide("a\xe2_") == L"a_");
 -
 -	// invalid 4-byte input
 -	CHECK(as_wide("a\xf2\x97\x98") == L"a");
 -	CHECK(as_wide("a\xf2\x97") == L"a");
 -	CHECK(as_wide("a\xf2") == L"a");
 -	CHECK(as_wide("a\xf2\x97\x98_") == L"a_");
 -	CHECK(as_wide("a\xf2\x97_") == L"a_");
 -	CHECK(as_wide("a\xf2_") == L"a_");
 -
 -	// invalid 5-byte input
 -	std::wstring b5 = as_wide("\xf8\nbcd");
 -	CHECK(b5 == L"\nbcd");
 -}
 -
 -TEST(as_utf8_empty)
 -{
 -	CHECK(as_utf8(L"") == "");
 -}
 -
 -TEST(as_utf8_valid_basic)
 -{
 -	// valid 1-byte, 2-byte and 3-byte outputs
 -#ifdef U_LITERALS
 -	CHECK(as_utf8(L"?\u0400\u203D") == "?\xd0\x80\xe2\x80\xbd");
 -#else
 -	CHECK(as_utf8(L"?\x0400\x203D") == "?\xd0\x80\xe2\x80\xbd");
 -#endif
 -}
 -
 -TEST(as_utf8_valid_astral)
 -{
 -	// valid 4-byte output
 -	size_t wcharsize = sizeof(wchar_t);
 -
 -	if (wcharsize == 4)
 -	{
 -		std::wstring s;
 -		s.resize(3);
 -		s[0] = wchar_cast(0x97624);
 -		s[1] = ' ';
 -		s[2] = wchar_cast(0x1003ff);
 -
 -		CHECK(as_utf8(s.c_str()) == "\xf2\x97\x98\xa4 \xf4\x80\x8f\xbf");
 -	}
 -	else
 -	{
 -	#ifdef U_LITERALS
 -		CHECK(as_utf8(L"\uda1d\ude24 \udbc0\udfff") == "\xf2\x97\x98\xa4 \xf4\x80\x8f\xbf");
 -	#else
 -		CHECK(as_utf8(L"\xda1d\xde24 \xdbc0\xdfff") == "\xf2\x97\x98\xa4 \xf4\x80\x8f\xbf");
 -	#endif
 -	}
 -}
 -
 -TEST(as_utf8_invalid)
 -{
 -	size_t wcharsize = sizeof(wchar_t);
 -
 -	if (wcharsize == 2)
 -	{
 -		// check non-terminated degenerate handling
 -	#ifdef U_LITERALS
 -		CHECK(as_utf8(L"a\uda1d") == "a");
 -		CHECK(as_utf8(L"a\uda1d_") == "a_");
 -	#else
 -		CHECK(as_utf8(L"a\xda1d") == "a");
 -		CHECK(as_utf8(L"a\xda1d_") == "a_");
 -	#endif
 -
 -		// check incorrect leading code
 -	#ifdef U_LITERALS
 -		CHECK(as_utf8(L"a\ude24") == "a");
 -		CHECK(as_utf8(L"a\ude24_") == "a_");
 -	#else
 -		CHECK(as_utf8(L"a\xde24") == "a");
 -		CHECK(as_utf8(L"a\xde24_") == "a_");
 -	#endif
 -	}
 -}
 -#endif
 +#ifndef PUGIXML_NO_STL + +#include "common.hpp" + +#include <string> + +// letters taken from http://www.utf8-chartable.de/ + +TEST(as_wide_empty) +{ +	CHECK(as_wide("") == L""); +} + +TEST(as_wide_valid_basic) +{ +	// valid 1-byte, 2-byte and 3-byte inputs +#ifdef U_LITERALS +	CHECK(as_wide("?\xd0\x80\xe2\x80\xbd") == L"?\u0400\u203D"); +#else +	CHECK(as_wide("?\xd0\x80\xe2\x80\xbd") == L"?\x0400\x203D"); +#endif +} + +TEST(as_wide_valid_astral) +{ +	// valid 4-byte input +	std::wstring b4 = as_wide("\xf2\x97\x98\xa4 \xf4\x80\x8f\xbf"); + +	size_t wcharsize = sizeof(wchar_t); + +	if (wcharsize == 4) +	{ +		CHECK(b4.size() == 3 && b4[0] == wchar_cast(0x97624) && b4[1] == L' ' && b4[2] == wchar_cast(0x1003ff)); +	} +	else +	{ +		CHECK(b4.size() == 5 && b4[0] == 0xda1d && b4[1] == 0xde24 && b4[2] == L' ' && b4[3] == 0xdbc0 && b4[4] == 0xdfff); +	} +} + +TEST(as_wide_invalid) +{ +	// invalid 1-byte input +	CHECK(as_wide("a\xb0") == L"a"); +	CHECK(as_wide("a\xb0_") == L"a_"); + +	// invalid 2-byte input +	CHECK(as_wide("a\xc0") == L"a"); +	CHECK(as_wide("a\xd0") == L"a"); +	CHECK(as_wide("a\xc0_") == L"a_"); +	CHECK(as_wide("a\xd0_") == L"a_"); + +	// invalid 3-byte input +	CHECK(as_wide("a\xe2\x80") == L"a"); +	CHECK(as_wide("a\xe2") == L"a"); +	CHECK(as_wide("a\xe2\x80_") == L"a_"); +	CHECK(as_wide("a\xe2_") == L"a_"); + +	// invalid 4-byte input +	CHECK(as_wide("a\xf2\x97\x98") == L"a"); +	CHECK(as_wide("a\xf2\x97") == L"a"); +	CHECK(as_wide("a\xf2") == L"a"); +	CHECK(as_wide("a\xf2\x97\x98_") == L"a_"); +	CHECK(as_wide("a\xf2\x97_") == L"a_"); +	CHECK(as_wide("a\xf2_") == L"a_"); + +	// invalid 5-byte input +	std::wstring b5 = as_wide("\xf8\nbcd"); +	CHECK(b5 == L"\nbcd"); +} + +TEST(as_utf8_empty) +{ +	CHECK(as_utf8(L"") == ""); +} + +TEST(as_utf8_valid_basic) +{ +	// valid 1-byte, 2-byte and 3-byte outputs +#ifdef U_LITERALS +	CHECK(as_utf8(L"?\u0400\u203D") == "?\xd0\x80\xe2\x80\xbd"); +#else +	CHECK(as_utf8(L"?\x0400\x203D") == "?\xd0\x80\xe2\x80\xbd"); +#endif +} + +TEST(as_utf8_valid_astral) +{ +	// valid 4-byte output +	size_t wcharsize = sizeof(wchar_t); + +	if (wcharsize == 4) +	{ +		std::wstring s; +		s.resize(3); +		s[0] = wchar_cast(0x97624); +		s[1] = ' '; +		s[2] = wchar_cast(0x1003ff); + +		CHECK(as_utf8(s.c_str()) == "\xf2\x97\x98\xa4 \xf4\x80\x8f\xbf"); +	} +	else +	{ +	#ifdef U_LITERALS +		CHECK(as_utf8(L"\uda1d\ude24 \udbc0\udfff") == "\xf2\x97\x98\xa4 \xf4\x80\x8f\xbf"); +	#else +		CHECK(as_utf8(L"\xda1d\xde24 \xdbc0\xdfff") == "\xf2\x97\x98\xa4 \xf4\x80\x8f\xbf"); +	#endif +	} +} + +TEST(as_utf8_invalid) +{ +	size_t wcharsize = sizeof(wchar_t); + +	if (wcharsize == 2) +	{ +		// check non-terminated degenerate handling +	#ifdef U_LITERALS +		CHECK(as_utf8(L"a\uda1d") == "a"); +		CHECK(as_utf8(L"a\uda1d_") == "a_"); +	#else +		CHECK(as_utf8(L"a\xda1d") == "a"); +		CHECK(as_utf8(L"a\xda1d_") == "a_"); +	#endif + +		// check incorrect leading code +	#ifdef U_LITERALS +		CHECK(as_utf8(L"a\ude24") == "a"); +		CHECK(as_utf8(L"a\ude24_") == "a_"); +	#else +		CHECK(as_utf8(L"a\xde24") == "a"); +		CHECK(as_utf8(L"a\xde24_") == "a_"); +	#endif +	} +} +#endif diff --git a/tests/test_write.cpp b/tests/test_write.cpp index cb75c74..b7d8412 100644 --- a/tests/test_write.cpp +++ b/tests/test_write.cpp @@ -1,354 +1,354 @@ -#include "common.hpp"
 -
 -#include "writer_string.hpp"
 -
 -#include <string>
 -#include <sstream>
 -
 -TEST_XML(write_simple, "<node attr='1'><child>text</child></node>")
 -{
 -	CHECK_NODE_EX(doc, STR("<node attr=\"1\">\n<child>text</child>\n</node>\n"), STR(""), 0);
 -}
 -
 -TEST_XML(write_raw, "<node attr='1'><child>text</child></node>")
 -{
 -	CHECK_NODE_EX(doc, STR("<node attr=\"1\"><child>text</child></node>"), STR(""), format_raw);
 -}
 -
 -TEST_XML(write_indent, "<node attr='1'><child><sub>text</sub></child></node>")
 -{
 -	CHECK_NODE_EX(doc, STR("<node attr=\"1\">\n\t<child>\n\t\t<sub>text</sub>\n\t</child>\n</node>\n"), STR("\t"), format_indent);
 -}
 -
 -TEST_XML(write_pcdata, "<node attr='1'><child><sub/>text</child></node>")
 -{
 -	CHECK_NODE_EX(doc, STR("<node attr=\"1\">\n\t<child>\n\t\t<sub />\n\t\ttext\n\t</child>\n</node>\n"), STR("\t"), format_indent);
 -}
 -
 -TEST_XML(write_cdata, "<![CDATA[value]]>")
 -{
 -	CHECK_NODE(doc, STR("<![CDATA[value]]>"));
 -	CHECK_NODE_EX(doc, STR("<![CDATA[value]]>\n"), STR(""), 0);
 -}
 -
 -TEST_XML_FLAGS(write_comment, "<!--text-->", parse_default | parse_comments)
 -{
 -	CHECK_NODE(doc, STR("<!--text-->"));
 -	CHECK_NODE_EX(doc, STR("<!--text-->\n"), STR(""), 0);
 -}
 -
 -TEST_XML_FLAGS(write_pi, "<?name value?>", parse_default | parse_pi)
 -{
 -	CHECK_NODE(doc, STR("<?name value?>"));
 -	CHECK_NODE_EX(doc, STR("<?name value?>\n"), STR(""), 0);
 -}
 -
 -TEST_XML_FLAGS(write_declaration, "<?xml version='2.0'?>", parse_default | parse_declaration)
 -{
 -	CHECK_NODE(doc, STR("<?xml version=\"2.0\"?>"));
 -	CHECK_NODE_EX(doc, STR("<?xml version=\"2.0\"?>\n"), STR(""), 0);
 -}
 -
 -TEST_XML(write_escape, "<node attr=''>text</node>")
 -{
 -	doc.child(STR("node")).attribute(STR("attr")) = STR("<>'\"&\x04\r\n\t");
 -	doc.child(STR("node")).first_child().set_value(STR("<>'\"&\x04\r\n\t"));
 -
 -	CHECK_NODE(doc, STR("<node attr=\"<>'"&
\t\"><>'\"&\r\n\t</node>"));
 -}
 -
 -TEST_XML(write_escape_unicode, "<node attr='㰀'/>")
 -{
 -#ifdef PUGIXML_WCHAR_MODE
 -	#ifdef U_LITERALS
 -		CHECK_NODE(doc, STR("<node attr=\"\u3c00\" />"));
 -	#else
 -		CHECK_NODE(doc, STR("<node attr=\"\x3c00\" />"));
 -	#endif
 -#else
 -	CHECK_NODE(doc, STR("<node attr=\"\xe3\xb0\x80\" />"));
 -#endif
 -}
 -
 -struct test_writer: xml_writer
 -{
 -	std::basic_string<pugi::char_t> contents;
 -
 -	virtual void write(const void* data, size_t size)
 -	{
 -		CHECK(size % sizeof(pugi::char_t) == 0);
 -		contents += std::basic_string<pugi::char_t>(static_cast<const pugi::char_t*>(data), static_cast<const pugi::char_t*>(data) + size / sizeof(pugi::char_t));
 -	}
 -};
 -
 -TEST_XML(write_print_writer, "<node/>")
 -{
 -	test_writer writer;
 -	doc.print(writer, STR(""), format_default, get_native_encoding());
 -
 -	CHECK(writer.contents == STR("<node />\n"));
 -}
 -
 -#ifndef PUGIXML_NO_STL
 -TEST_XML(write_print_stream, "<node/>")
 -{
 -	std::ostringstream oss;
 -	doc.print(oss, STR(""), format_default, encoding_utf8);
 -
 -	CHECK(oss.str() == "<node />\n");
 -}
 -
 -TEST_XML(write_print_stream_encode, "<n/>")
 -{
 -	std::ostringstream oss;
 -	doc.print(oss, STR(""), format_default, encoding_utf16_be);
 -
 -	CHECK(oss.str() == std::string("\x00<\x00n\x00 \x00/\x00>\x00\n", 12));
 -}
 -
 -TEST_XML(write_print_stream_wide, "<node/>")
 -{
 -	std::basic_ostringstream<wchar_t> oss;
 -	doc.print(oss, STR(""), format_default, encoding_utf8);
 -
 -	CHECK(oss.str() == L"<node />\n");
 -}
 -#endif
 -
 -TEST_XML(write_huge_chunk, "<node/>")
 -{
 -	std::basic_string<pugi::char_t> name(10000, STR('n'));
 -	doc.child(STR("node")).set_name(name.c_str());
 -
 -	test_writer writer;
 -	doc.print(writer, STR(""), format_default, get_native_encoding());
 -
 -	CHECK(writer.contents == STR("<") + name + STR(" />\n"));
 -}
 -
 -TEST(write_encodings)
 -{
 -	static char s_utf8[] = "<\x54\xC2\xA2\xE2\x82\xAC\xF0\xA4\xAD\xA2/>";
 -
 -	xml_document doc;
 -	CHECK(doc.load_buffer(s_utf8, sizeof(s_utf8), parse_default, encoding_utf8));
 -
 -	CHECK(write_narrow(doc, format_default, encoding_utf8) == "<\x54\xC2\xA2\xE2\x82\xAC\xF0\xA4\xAD\xA2 />\n");
 -
 -	CHECK(test_write_narrow(doc, format_default, encoding_utf32_le, "<\x00\x00\x00\x54\x00\x00\x00\xA2\x00\x00\x00\xAC\x20\x00\x00\x62\x4B\x02\x00 \x00\x00\x00/\x00\x00\x00>\x00\x00\x00\n\x00\x00\x00", 36));
 -	CHECK(test_write_narrow(doc, format_default, encoding_utf32_be, "\x00\x00\x00<\x00\x00\x00\x54\x00\x00\x00\xA2\x00\x00\x20\xAC\x00\x02\x4B\x62\x00\x00\x00 \x00\x00\x00/\x00\x00\x00>\x00\x00\x00\n", 36));
 -	CHECK(write_narrow(doc, format_default, encoding_utf32) == write_narrow(doc, format_default, is_little_endian() ? encoding_utf32_le : encoding_utf32_be));
 -
 -	CHECK(test_write_narrow(doc, format_default, encoding_utf16_le, "<\x00\x54\x00\xA2\x00\xAC\x20\x52\xd8\x62\xdf \x00/\x00>\x00\n\x00", 20));
 -	CHECK(test_write_narrow(doc, format_default, encoding_utf16_be, "\x00<\x00\x54\x00\xA2\x20\xAC\xd8\x52\xdf\x62\x00 \x00/\x00>\x00\n", 20));
 -	CHECK(write_narrow(doc, format_default, encoding_utf16) == write_narrow(doc, format_default, is_little_endian() ? encoding_utf16_le : encoding_utf16_be));
 -
 -	size_t wcharsize = sizeof(wchar_t);
 -	std::wstring v = write_wide(doc, format_default, encoding_wchar);
 -
 -	if (wcharsize == 4)
 -	{
 -		CHECK(v.size() == 9 && v[0] == '<' && v[1] == 0x54 && v[2] == 0xA2 && v[3] == 0x20AC && v[4] == wchar_cast(0x24B62) && v[5] == ' ' && v[6] == '/' && v[7] == '>' && v[8] == '\n');
 -	}
 -	else
 -	{
 -		CHECK(v.size() == 10 && v[0] == '<' && v[1] == 0x54 && v[2] == 0xA2 && v[3] == 0x20AC && v[4] == 0xd852 && v[5] == 0xdf62 && v[6] == ' ' && v[7] == '/' && v[8] == '>' && v[9] == '\n');
 -	}
 -}
 -
 -#ifdef PUGIXML_WCHAR_MODE
 -TEST(write_encoding_huge)
 -{
 -	const unsigned int N = 16000;
 -
 -	// make a large utf16 name consisting of 6-byte char pairs (6 does not divide internal buffer size, so will need split correction)
 -	std::string s_utf16 = std::string("\x00<", 2);
 -
 -	for (unsigned int i = 0; i < N; ++i) s_utf16 += "\x20\xAC\xd8\x52\xdf\x62";
 -
 -	s_utf16 += std::string("\x00/\x00>", 4);
 -
 -	xml_document doc;
 -	CHECK(doc.load_buffer(&s_utf16[0], s_utf16.length(), parse_default, encoding_utf16_be));
 -
 -	std::string s_utf8 = "<";
 -
 -	for (unsigned int j = 0; j < N; ++j) s_utf8 += "\xE2\x82\xAC\xF0\xA4\xAD\xA2";
 -
 -	s_utf8 += " />\n";
 -
 -	CHECK(test_write_narrow(doc, format_default, encoding_utf8, s_utf8.c_str(), s_utf8.length()));
 -}
 -
 -TEST(write_encoding_huge_invalid)
 -{
 -	size_t wcharsize = sizeof(wchar_t);
 -
 -	if (wcharsize == 2)
 -	{
 -		const unsigned int N = 16000;
 -
 -		// make a large utf16 name consisting of leading surrogate chars
 -		std::basic_string<wchar_t> s_utf16;
 -
 -		for (unsigned int i = 0; i < N; ++i) s_utf16 += static_cast<wchar_t>(0xd852);
 -
 -		xml_document doc;
 -		doc.append_child().set_name(s_utf16.c_str());
 -
 -		CHECK(test_write_narrow(doc, format_default, encoding_utf8, "< />\n", 5));
 -	}
 -}
 -#else
 -TEST(write_encoding_huge)
 -{
 -	const unsigned int N = 16000;
 -
 -	// make a large utf8 name consisting of 3-byte chars (3 does not divide internal buffer size, so will need split correction)
 -	std::string s_utf8 = "<";
 -
 -	for (unsigned int i = 0; i < N; ++i) s_utf8 += "\xE2\x82\xAC";
 -
 -	s_utf8 += "/>";
 -
 -	xml_document doc;
 -	CHECK(doc.load_buffer(&s_utf8[0], s_utf8.length(), parse_default, encoding_utf8));
 -
 -	std::string s_utf16 = std::string("\x00<", 2);
 -
 -	for (unsigned int j = 0; j < N; ++j) s_utf16 += "\x20\xAC";
 -
 -	s_utf16 += std::string("\x00 \x00/\x00>\x00\n", 8);
 -
 -	CHECK(test_write_narrow(doc, format_default, encoding_utf16_be, s_utf16.c_str(), s_utf16.length()));
 -}
 -
 -TEST(write_encoding_huge_invalid)
 -{
 -	const unsigned int N = 16000;
 -
 -	// make a large utf8 name consisting of non-leading chars
 -	std::string s_utf8;
 -
 -	for (unsigned int i = 0; i < N; ++i) s_utf8 += "\x82";
 -
 -	xml_document doc;
 -	doc.append_child().set_name(s_utf8.c_str());
 -
 -	std::string s_utf16 = std::string("\x00<\x00 \x00/\x00>\x00\n", 10);
 -
 -	CHECK(test_write_narrow(doc, format_default, encoding_utf16_be, s_utf16.c_str(), s_utf16.length()));
 -}
 -#endif
 -
 -TEST(write_unicode_escape)
 -{
 -	char s_utf8[] = "<\xE2\x82\xAC \xC2\xA2='\"\xF0\xA4\xAD\xA2
\"'>&\x14\xF0\xA4\xAD\xA2<</\xE2\x82\xAC>";
 -	
 -	xml_document doc;
 -	CHECK(doc.load_buffer(s_utf8, sizeof(s_utf8), parse_default, encoding_utf8));
 -
 -	CHECK(write_narrow(doc, format_default, encoding_utf8) == "<\xE2\x82\xAC \xC2\xA2=\""\xF0\xA4\xAD\xA2
"\">&\xF0\xA4\xAD\xA2<</\xE2\x82\xAC>\n");
 -}
 -
 -#ifdef PUGIXML_WCHAR_MODE
 -static bool test_write_unicode_invalid(const wchar_t* name, const char* expected)
 -{
 -	xml_document doc;
 -	doc.append_child(node_pcdata).set_value(name);
 -
 -	return write_narrow(doc, format_raw, encoding_utf8) == expected;
 -}
 -
 -TEST(write_unicode_invalid_utf16)
 -{
 -	size_t wcharsize = sizeof(wchar_t);
 -
 -	if (wcharsize == 2)
 -	{
 -		// check non-terminated degenerate handling
 -	#ifdef U_LITERALS
 -		CHECK(test_write_unicode_invalid(L"a\uda1d", "a"));
 -		CHECK(test_write_unicode_invalid(L"a\uda1d_", "a_"));
 -	#else
 -		CHECK(test_write_unicode_invalid(L"a\xda1d", "a"));
 -		CHECK(test_write_unicode_invalid(L"a\xda1d_", "a_"));
 -	#endif
 -
 -		// check incorrect leading code
 -	#ifdef U_LITERALS
 -		CHECK(test_write_unicode_invalid(L"a\ude24", "a"));
 -		CHECK(test_write_unicode_invalid(L"a\ude24_", "a_"));
 -	#else
 -		CHECK(test_write_unicode_invalid(L"a\xde24", "a"));
 -		CHECK(test_write_unicode_invalid(L"a\xde24_", "a_"));
 -	#endif
 -	}
 -}
 -#else
 -static bool test_write_unicode_invalid(const char* name, const wchar_t* expected)
 -{
 -	xml_document doc;
 -	doc.append_child(node_pcdata).set_value(name);
 -
 -	return write_wide(doc, format_raw, encoding_wchar) == expected;
 -}
 -
 -TEST(write_unicode_invalid_utf8)
 -{
 -	// invalid 1-byte input
 -	CHECK(test_write_unicode_invalid("a\xb0", L"a"));
 -	CHECK(test_write_unicode_invalid("a\xb0_", L"a_"));
 -
 -	// invalid 2-byte input
 -	CHECK(test_write_unicode_invalid("a\xc0", L"a"));
 -	CHECK(test_write_unicode_invalid("a\xd0", L"a"));
 -	CHECK(test_write_unicode_invalid("a\xc0_", L"a_"));
 -	CHECK(test_write_unicode_invalid("a\xd0_", L"a_"));
 -
 -	// invalid 3-byte input
 -	CHECK(test_write_unicode_invalid("a\xe2\x80", L"a"));
 -	CHECK(test_write_unicode_invalid("a\xe2", L"a"));
 -	CHECK(test_write_unicode_invalid("a\xe2\x80_", L"a_"));
 -	CHECK(test_write_unicode_invalid("a\xe2_", L"a_"));
 -
 -	// invalid 4-byte input
 -	CHECK(test_write_unicode_invalid("a\xf2\x97\x98", L"a"));
 -	CHECK(test_write_unicode_invalid("a\xf2\x97", L"a"));
 -	CHECK(test_write_unicode_invalid("a\xf2", L"a"));
 -	CHECK(test_write_unicode_invalid("a\xf2\x97\x98_", L"a_"));
 -	CHECK(test_write_unicode_invalid("a\xf2\x97_", L"a_"));
 -	CHECK(test_write_unicode_invalid("a\xf2_", L"a_"));
 -
 -	// invalid 5-byte input
 -	CHECK(test_write_unicode_invalid("a\xf8_", L"a_"));
 -}
 -#endif
 -
 -TEST(write_no_name_element)
 -{
 -	xml_document doc;
 -	xml_node root = doc.append_child();
 -	root.append_child();
 -	root.append_child().append_child(node_pcdata).set_value(STR("text"));
 -
 -	CHECK_NODE(doc, STR("<:anonymous><:anonymous /><:anonymous>text</:anonymous></:anonymous>"));
 -	CHECK_NODE_EX(doc, STR("<:anonymous>\n\t<:anonymous />\n\t<:anonymous>text</:anonymous>\n</:anonymous>\n"), STR("\t"), format_default);
 -}
 -
 -TEST(write_no_name_pi)
 -{
 -	xml_document doc;
 -	doc.append_child(node_pi);
 -
 -	CHECK_NODE(doc, STR("<?:anonymous?>"));
 -}
 -
 -TEST(write_no_name_attribute)
 -{
 -	xml_document doc;
 -	doc.append_child().set_name(STR("root"));
 -	doc.child(STR("root")).append_attribute(STR(""));
 -
 -	CHECK_NODE(doc, STR("<root :anonymous=\"\" />"));
 -}
 +#include "common.hpp" + +#include "writer_string.hpp" + +#include <string> +#include <sstream> + +TEST_XML(write_simple, "<node attr='1'><child>text</child></node>") +{ +	CHECK_NODE_EX(doc, STR("<node attr=\"1\">\n<child>text</child>\n</node>\n"), STR(""), 0); +} + +TEST_XML(write_raw, "<node attr='1'><child>text</child></node>") +{ +	CHECK_NODE_EX(doc, STR("<node attr=\"1\"><child>text</child></node>"), STR(""), format_raw); +} + +TEST_XML(write_indent, "<node attr='1'><child><sub>text</sub></child></node>") +{ +	CHECK_NODE_EX(doc, STR("<node attr=\"1\">\n\t<child>\n\t\t<sub>text</sub>\n\t</child>\n</node>\n"), STR("\t"), format_indent); +} + +TEST_XML(write_pcdata, "<node attr='1'><child><sub/>text</child></node>") +{ +	CHECK_NODE_EX(doc, STR("<node attr=\"1\">\n\t<child>\n\t\t<sub />\n\t\ttext\n\t</child>\n</node>\n"), STR("\t"), format_indent); +} + +TEST_XML(write_cdata, "<![CDATA[value]]>") +{ +	CHECK_NODE(doc, STR("<![CDATA[value]]>")); +	CHECK_NODE_EX(doc, STR("<![CDATA[value]]>\n"), STR(""), 0); +} + +TEST_XML_FLAGS(write_comment, "<!--text-->", parse_default | parse_comments) +{ +	CHECK_NODE(doc, STR("<!--text-->")); +	CHECK_NODE_EX(doc, STR("<!--text-->\n"), STR(""), 0); +} + +TEST_XML_FLAGS(write_pi, "<?name value?>", parse_default | parse_pi) +{ +	CHECK_NODE(doc, STR("<?name value?>")); +	CHECK_NODE_EX(doc, STR("<?name value?>\n"), STR(""), 0); +} + +TEST_XML_FLAGS(write_declaration, "<?xml version='2.0'?>", parse_default | parse_declaration) +{ +	CHECK_NODE(doc, STR("<?xml version=\"2.0\"?>")); +	CHECK_NODE_EX(doc, STR("<?xml version=\"2.0\"?>\n"), STR(""), 0); +} + +TEST_XML(write_escape, "<node attr=''>text</node>") +{ +	doc.child(STR("node")).attribute(STR("attr")) = STR("<>'\"&\x04\r\n\t"); +	doc.child(STR("node")).first_child().set_value(STR("<>'\"&\x04\r\n\t")); + +	CHECK_NODE(doc, STR("<node attr=\"<>'"&
\t\"><>'\"&\r\n\t</node>")); +} + +TEST_XML(write_escape_unicode, "<node attr='㰀'/>") +{ +#ifdef PUGIXML_WCHAR_MODE +	#ifdef U_LITERALS +		CHECK_NODE(doc, STR("<node attr=\"\u3c00\" />")); +	#else +		CHECK_NODE(doc, STR("<node attr=\"\x3c00\" />")); +	#endif +#else +	CHECK_NODE(doc, STR("<node attr=\"\xe3\xb0\x80\" />")); +#endif +} + +struct test_writer: xml_writer +{ +	std::basic_string<pugi::char_t> contents; + +	virtual void write(const void* data, size_t size) +	{ +		CHECK(size % sizeof(pugi::char_t) == 0); +		contents += std::basic_string<pugi::char_t>(static_cast<const pugi::char_t*>(data), static_cast<const pugi::char_t*>(data) + size / sizeof(pugi::char_t)); +	} +}; + +TEST_XML(write_print_writer, "<node/>") +{ +	test_writer writer; +	doc.print(writer, STR(""), format_default, get_native_encoding()); + +	CHECK(writer.contents == STR("<node />\n")); +} + +#ifndef PUGIXML_NO_STL +TEST_XML(write_print_stream, "<node/>") +{ +	std::ostringstream oss; +	doc.print(oss, STR(""), format_default, encoding_utf8); + +	CHECK(oss.str() == "<node />\n"); +} + +TEST_XML(write_print_stream_encode, "<n/>") +{ +	std::ostringstream oss; +	doc.print(oss, STR(""), format_default, encoding_utf16_be); + +	CHECK(oss.str() == std::string("\x00<\x00n\x00 \x00/\x00>\x00\n", 12)); +} + +TEST_XML(write_print_stream_wide, "<node/>") +{ +	std::basic_ostringstream<wchar_t> oss; +	doc.print(oss, STR(""), format_default, encoding_utf8); + +	CHECK(oss.str() == L"<node />\n"); +} +#endif + +TEST_XML(write_huge_chunk, "<node/>") +{ +	std::basic_string<pugi::char_t> name(10000, STR('n')); +	doc.child(STR("node")).set_name(name.c_str()); + +	test_writer writer; +	doc.print(writer, STR(""), format_default, get_native_encoding()); + +	CHECK(writer.contents == STR("<") + name + STR(" />\n")); +} + +TEST(write_encodings) +{ +	static char s_utf8[] = "<\x54\xC2\xA2\xE2\x82\xAC\xF0\xA4\xAD\xA2/>"; + +	xml_document doc; +	CHECK(doc.load_buffer(s_utf8, sizeof(s_utf8), parse_default, encoding_utf8)); + +	CHECK(write_narrow(doc, format_default, encoding_utf8) == "<\x54\xC2\xA2\xE2\x82\xAC\xF0\xA4\xAD\xA2 />\n"); + +	CHECK(test_write_narrow(doc, format_default, encoding_utf32_le, "<\x00\x00\x00\x54\x00\x00\x00\xA2\x00\x00\x00\xAC\x20\x00\x00\x62\x4B\x02\x00 \x00\x00\x00/\x00\x00\x00>\x00\x00\x00\n\x00\x00\x00", 36)); +	CHECK(test_write_narrow(doc, format_default, encoding_utf32_be, "\x00\x00\x00<\x00\x00\x00\x54\x00\x00\x00\xA2\x00\x00\x20\xAC\x00\x02\x4B\x62\x00\x00\x00 \x00\x00\x00/\x00\x00\x00>\x00\x00\x00\n", 36)); +	CHECK(write_narrow(doc, format_default, encoding_utf32) == write_narrow(doc, format_default, is_little_endian() ? encoding_utf32_le : encoding_utf32_be)); + +	CHECK(test_write_narrow(doc, format_default, encoding_utf16_le, "<\x00\x54\x00\xA2\x00\xAC\x20\x52\xd8\x62\xdf \x00/\x00>\x00\n\x00", 20)); +	CHECK(test_write_narrow(doc, format_default, encoding_utf16_be, "\x00<\x00\x54\x00\xA2\x20\xAC\xd8\x52\xdf\x62\x00 \x00/\x00>\x00\n", 20)); +	CHECK(write_narrow(doc, format_default, encoding_utf16) == write_narrow(doc, format_default, is_little_endian() ? encoding_utf16_le : encoding_utf16_be)); + +	size_t wcharsize = sizeof(wchar_t); +	std::wstring v = write_wide(doc, format_default, encoding_wchar); + +	if (wcharsize == 4) +	{ +		CHECK(v.size() == 9 && v[0] == '<' && v[1] == 0x54 && v[2] == 0xA2 && v[3] == 0x20AC && v[4] == wchar_cast(0x24B62) && v[5] == ' ' && v[6] == '/' && v[7] == '>' && v[8] == '\n'); +	} +	else +	{ +		CHECK(v.size() == 10 && v[0] == '<' && v[1] == 0x54 && v[2] == 0xA2 && v[3] == 0x20AC && v[4] == 0xd852 && v[5] == 0xdf62 && v[6] == ' ' && v[7] == '/' && v[8] == '>' && v[9] == '\n'); +	} +} + +#ifdef PUGIXML_WCHAR_MODE +TEST(write_encoding_huge) +{ +	const unsigned int N = 16000; + +	// make a large utf16 name consisting of 6-byte char pairs (6 does not divide internal buffer size, so will need split correction) +	std::string s_utf16 = std::string("\x00<", 2); + +	for (unsigned int i = 0; i < N; ++i) s_utf16 += "\x20\xAC\xd8\x52\xdf\x62"; + +	s_utf16 += std::string("\x00/\x00>", 4); + +	xml_document doc; +	CHECK(doc.load_buffer(&s_utf16[0], s_utf16.length(), parse_default, encoding_utf16_be)); + +	std::string s_utf8 = "<"; + +	for (unsigned int j = 0; j < N; ++j) s_utf8 += "\xE2\x82\xAC\xF0\xA4\xAD\xA2"; + +	s_utf8 += " />\n"; + +	CHECK(test_write_narrow(doc, format_default, encoding_utf8, s_utf8.c_str(), s_utf8.length())); +} + +TEST(write_encoding_huge_invalid) +{ +	size_t wcharsize = sizeof(wchar_t); + +	if (wcharsize == 2) +	{ +		const unsigned int N = 16000; + +		// make a large utf16 name consisting of leading surrogate chars +		std::basic_string<wchar_t> s_utf16; + +		for (unsigned int i = 0; i < N; ++i) s_utf16 += static_cast<wchar_t>(0xd852); + +		xml_document doc; +		doc.append_child().set_name(s_utf16.c_str()); + +		CHECK(test_write_narrow(doc, format_default, encoding_utf8, "< />\n", 5)); +	} +} +#else +TEST(write_encoding_huge) +{ +	const unsigned int N = 16000; + +	// make a large utf8 name consisting of 3-byte chars (3 does not divide internal buffer size, so will need split correction) +	std::string s_utf8 = "<"; + +	for (unsigned int i = 0; i < N; ++i) s_utf8 += "\xE2\x82\xAC"; + +	s_utf8 += "/>"; + +	xml_document doc; +	CHECK(doc.load_buffer(&s_utf8[0], s_utf8.length(), parse_default, encoding_utf8)); + +	std::string s_utf16 = std::string("\x00<", 2); + +	for (unsigned int j = 0; j < N; ++j) s_utf16 += "\x20\xAC"; + +	s_utf16 += std::string("\x00 \x00/\x00>\x00\n", 8); + +	CHECK(test_write_narrow(doc, format_default, encoding_utf16_be, s_utf16.c_str(), s_utf16.length())); +} + +TEST(write_encoding_huge_invalid) +{ +	const unsigned int N = 16000; + +	// make a large utf8 name consisting of non-leading chars +	std::string s_utf8; + +	for (unsigned int i = 0; i < N; ++i) s_utf8 += "\x82"; + +	xml_document doc; +	doc.append_child().set_name(s_utf8.c_str()); + +	std::string s_utf16 = std::string("\x00<\x00 \x00/\x00>\x00\n", 10); + +	CHECK(test_write_narrow(doc, format_default, encoding_utf16_be, s_utf16.c_str(), s_utf16.length())); +} +#endif + +TEST(write_unicode_escape) +{ +	char s_utf8[] = "<\xE2\x82\xAC \xC2\xA2='\"\xF0\xA4\xAD\xA2
\"'>&\x14\xF0\xA4\xAD\xA2<</\xE2\x82\xAC>"; +	 +	xml_document doc; +	CHECK(doc.load_buffer(s_utf8, sizeof(s_utf8), parse_default, encoding_utf8)); + +	CHECK(write_narrow(doc, format_default, encoding_utf8) == "<\xE2\x82\xAC \xC2\xA2=\""\xF0\xA4\xAD\xA2
"\">&\xF0\xA4\xAD\xA2<</\xE2\x82\xAC>\n"); +} + +#ifdef PUGIXML_WCHAR_MODE +static bool test_write_unicode_invalid(const wchar_t* name, const char* expected) +{ +	xml_document doc; +	doc.append_child(node_pcdata).set_value(name); + +	return write_narrow(doc, format_raw, encoding_utf8) == expected; +} + +TEST(write_unicode_invalid_utf16) +{ +	size_t wcharsize = sizeof(wchar_t); + +	if (wcharsize == 2) +	{ +		// check non-terminated degenerate handling +	#ifdef U_LITERALS +		CHECK(test_write_unicode_invalid(L"a\uda1d", "a")); +		CHECK(test_write_unicode_invalid(L"a\uda1d_", "a_")); +	#else +		CHECK(test_write_unicode_invalid(L"a\xda1d", "a")); +		CHECK(test_write_unicode_invalid(L"a\xda1d_", "a_")); +	#endif + +		// check incorrect leading code +	#ifdef U_LITERALS +		CHECK(test_write_unicode_invalid(L"a\ude24", "a")); +		CHECK(test_write_unicode_invalid(L"a\ude24_", "a_")); +	#else +		CHECK(test_write_unicode_invalid(L"a\xde24", "a")); +		CHECK(test_write_unicode_invalid(L"a\xde24_", "a_")); +	#endif +	} +} +#else +static bool test_write_unicode_invalid(const char* name, const wchar_t* expected) +{ +	xml_document doc; +	doc.append_child(node_pcdata).set_value(name); + +	return write_wide(doc, format_raw, encoding_wchar) == expected; +} + +TEST(write_unicode_invalid_utf8) +{ +	// invalid 1-byte input +	CHECK(test_write_unicode_invalid("a\xb0", L"a")); +	CHECK(test_write_unicode_invalid("a\xb0_", L"a_")); + +	// invalid 2-byte input +	CHECK(test_write_unicode_invalid("a\xc0", L"a")); +	CHECK(test_write_unicode_invalid("a\xd0", L"a")); +	CHECK(test_write_unicode_invalid("a\xc0_", L"a_")); +	CHECK(test_write_unicode_invalid("a\xd0_", L"a_")); + +	// invalid 3-byte input +	CHECK(test_write_unicode_invalid("a\xe2\x80", L"a")); +	CHECK(test_write_unicode_invalid("a\xe2", L"a")); +	CHECK(test_write_unicode_invalid("a\xe2\x80_", L"a_")); +	CHECK(test_write_unicode_invalid("a\xe2_", L"a_")); + +	// invalid 4-byte input +	CHECK(test_write_unicode_invalid("a\xf2\x97\x98", L"a")); +	CHECK(test_write_unicode_invalid("a\xf2\x97", L"a")); +	CHECK(test_write_unicode_invalid("a\xf2", L"a")); +	CHECK(test_write_unicode_invalid("a\xf2\x97\x98_", L"a_")); +	CHECK(test_write_unicode_invalid("a\xf2\x97_", L"a_")); +	CHECK(test_write_unicode_invalid("a\xf2_", L"a_")); + +	// invalid 5-byte input +	CHECK(test_write_unicode_invalid("a\xf8_", L"a_")); +} +#endif + +TEST(write_no_name_element) +{ +	xml_document doc; +	xml_node root = doc.append_child(); +	root.append_child(); +	root.append_child().append_child(node_pcdata).set_value(STR("text")); + +	CHECK_NODE(doc, STR("<:anonymous><:anonymous /><:anonymous>text</:anonymous></:anonymous>")); +	CHECK_NODE_EX(doc, STR("<:anonymous>\n\t<:anonymous />\n\t<:anonymous>text</:anonymous>\n</:anonymous>\n"), STR("\t"), format_default); +} + +TEST(write_no_name_pi) +{ +	xml_document doc; +	doc.append_child(node_pi); + +	CHECK_NODE(doc, STR("<?:anonymous?>")); +} + +TEST(write_no_name_attribute) +{ +	xml_document doc; +	doc.append_child().set_name(STR("root")); +	doc.child(STR("root")).append_attribute(STR("")); + +	CHECK_NODE(doc, STR("<root :anonymous=\"\" />")); +} diff --git a/tests/test_xpath.cpp b/tests/test_xpath.cpp index 7b52437..608859f 100644 --- a/tests/test_xpath.cpp +++ b/tests/test_xpath.cpp @@ -1,227 +1,227 @@ -#ifndef PUGIXML_NO_XPATH
 -
 -#include "common.hpp"
 -
 -#include <float.h>
 -#include <string.h>
 -#include <wchar.h>
 -
 -#include <string>
 -
 -TEST(xpath_allocator_many_pages)
 -{
 -	pugi::string_t query = STR("0");
 -
 -	for (int i = 0; i < 128; ++i) query += STR("+string-length('abcdefgh')");
 -
 -	CHECK_XPATH_NUMBER(xml_node(), query.c_str(), 1024);
 -}
 -
 -TEST(xpath_allocator_large_page)
 -{
 -	pugi::string_t query;
 -
 -	for (int i = 0; i < 1024; ++i) query += STR("abcdefgh");
 -
 -	CHECK_XPATH_NUMBER(xml_node(), (STR("string-length('") + query + STR("')")).c_str(), 8192);
 -}
 -
 -TEST_XML(xpath_sort_complex, "<node><child1 attr1='value1' attr2='value2'/><child2 attr1='value1'>test</child2></node>")
 -{
 -	// just some random union order, it should not matter probably?
 -	xpath_node_set ns = doc.child(STR("node")).select_nodes(STR("child1 | child2 | child1/@* | . | child2/@* | child2/text()"));
 -
 -	ns.sort(false);
 -	xpath_node_set sorted = ns;
 -
 -	ns.sort(true);
 -	xpath_node_set reverse_sorted = ns;
 -
 -	xpath_node_set_tester(sorted, "sorted order failed") % 2 % 3 % 4 % 5 % 6 % 7 % 8;
 -	xpath_node_set_tester(reverse_sorted, "reverse sorted order failed") % 8 % 7 % 6 % 5 % 4 % 3 % 2;
 -}
 -
 -TEST_XML(xpath_sort_children, "<node><child><subchild id='1'/></child><child><subchild id='2'/></child></node>")
 -{
 -	xpath_node_set ns = doc.child(STR("node")).select_nodes(STR("child/subchild[@id=1] | child/subchild[@id=2]"));
 -
 -	ns.sort(false);
 -	xpath_node_set sorted = ns;
 -
 -	ns.sort(true);
 -	xpath_node_set reverse_sorted = ns;
 -
 -	xpath_node_set_tester(sorted, "sorted order failed") % 4 % 7;
 -	xpath_node_set_tester(reverse_sorted, "reverse sorted order failed") % 7 % 4;
 -}
 -
 -TEST_XML(xpath_sort_attributes, "<node/>")
 -{
 -	xml_node n = doc.child(STR("node"));
 -
 -	// we need to insert attributes manually since unsorted node sets are (always?) sorted via pointers because of remove_duplicates,
 -	// so we need to have different document and pointer order to cover all comparator cases
 -	n.append_attribute(STR("attr2"));
 -	n.append_attribute(STR("attr3"));
 -	n.insert_attribute_before(STR("attr1"), n.attribute(STR("attr2")));
 -
 -	xpath_node_set ns = n.select_nodes(STR("@*"));
 -
 -	ns.sort(true);
 -	xpath_node_set reverse_sorted = ns;
 -
 -	ns.sort(false);
 -	xpath_node_set sorted = ns;
 -
 -	xpath_node_set_tester(sorted, "sorted order failed") % 3 % 4 % 5;
 -	xpath_node_set_tester(reverse_sorted, "reverse sorted order failed") % 5 % 4 % 3;
 -}
 -
 -TEST(xpath_long_numbers_parse)
 -{
 -	const pugi::char_t* str_flt_max = STR("340282346638528860000000000000000000000");
 -	const pugi::char_t* str_flt_max_dec = STR("340282346638528860000000000000000000000.000000");
 -	
 -	const pugi::char_t* str_dbl_max = STR("179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
 -	const pugi::char_t* str_dbl_max_dec = STR("179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000");
 -
 -	xml_node c;
 -
 -	// check parsing
 -	CHECK_XPATH_NUMBER(c, str_flt_max, FLT_MAX);
 -	CHECK_XPATH_NUMBER(c, str_flt_max_dec, FLT_MAX);
 -	CHECK_XPATH_NUMBER(c, str_dbl_max, DBL_MAX);
 -	CHECK_XPATH_NUMBER(c, str_dbl_max_dec, DBL_MAX);
 -}
 -
 -static bool test_xpath_string_prefix(const pugi::xml_node& node, const pugi::char_t* query, const pugi::char_t* expected, size_t match_length)
 -{
 -#ifdef PUGIXML_WCHAR_MODE
 -	size_t expected_length = wcslen(expected);
 -#else
 -	size_t expected_length = strlen(expected);
 -#endif
 -
 -	pugi::xpath_query q(query);
 -	pugi::string_t value = q.evaluate_string(node);
 -
 -	return value.length() == expected_length && value.compare(0, match_length, expected, match_length) == 0;
 -}
 -
 -TEST(xpath_long_numbers_stringize)
 -{
 -	const pugi::char_t* str_flt_max = STR("340282346638528860000000000000000000000");
 -	const pugi::char_t* str_flt_max_dec = STR("340282346638528860000000000000000000000.000000");
 -	
 -	const pugi::char_t* str_dbl_max = STR("179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
 -	const pugi::char_t* str_dbl_max_dec = STR("179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000");
 -
 -	xml_node c;
 -
 -	CHECK(test_xpath_string_prefix(c, str_flt_max, str_flt_max, 15));
 -	CHECK(test_xpath_string_prefix(c, str_flt_max_dec, str_flt_max, 15));
 -
 -	CHECK(test_xpath_string_prefix(c, str_dbl_max, str_dbl_max, 15));
 -	CHECK(test_xpath_string_prefix(c, str_dbl_max_dec, str_dbl_max, 15));
 -}
 -
 -#include <stdio.h>
 -
 -TEST(xpath_denorm_numbers)
 -{
 -	pugi::string_t query;
 -
 -	// 10^-318 - double denormal
 -	for (int i = 0; i < 106; ++i)
 -	{
 -		if (i != 0) query += STR(" * ");
 -		query += STR("0.001");
 -	}
 -
 -	CHECK_XPATH_STRING(xml_node(), query.c_str(), STR("0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009999987484955998"));
 -}
 -
 -TEST_XML(xpath_rexml_1, "<a><b><c id='a'/></b><c id='b'/></a>")
 -{
 -	CHECK_XPATH_NODESET(doc, STR("//*[local-name()='c' and @id='b']")) % 6;
 -	CHECK_XPATH_NODESET(doc, STR("//*[ local-name()='c' and @id='b' ]")) % 6;
 -	CHECK_XPATH_NODESET(doc, STR("/a/c[@id]")) % 6;
 -	CHECK_XPATH_NODESET(doc, STR("/a/c[(@id)]")) % 6;
 -	CHECK_XPATH_NODESET(doc, STR("/a/c[ @id ]")) % 6;
 -	CHECK_XPATH_NODESET(doc, STR("/a/c[ (@id) ]")) % 6;
 -	CHECK_XPATH_NODESET(doc, STR("/a/c[( @id )]")) % 6;
 -	CHECK_XPATH_NODESET(doc, STR("/a/c[ ( @id ) ]")) % 6;
 -	CHECK_XPATH_NODESET(doc, STR("/a/c [ ( @id ) ] ")) % 6;
 -	CHECK_XPATH_NODESET(doc, STR(" / a / c [ ( @id ) ] ")) % 6;
 -}
 -
 -TEST_XML(xpath_rexml_2, "<a:x xmlns:a='1'><a:y p='p' q='q'><a:z>zzz</a:z></a:y></a:x>")
 -{
 -	CHECK_XPATH_NODESET(doc, STR("a:x/a:y[@p='p' and @q='q']/a:z/text()")) % 8;
 -}
 -
 -TEST_XML(xpath_rexml_3, "<article><section role='subdivision' id='1'><para>free flowing text.</para></section><section role='division'><section role='subdivision' id='2'><para>free flowing text.</para></section><section role='division'><para>free flowing text.</para></section></section></article>")
 -{
 -	CHECK_XPATH_NODESET(doc, STR("//section[../self::section[@role=\"division\"]]")) % 10 % 15;
 -    CHECK_XPATH_NODESET(doc, STR("//section[@role=\"subdivision\" and not(../self::section[@role=\"division\"])]")) % 3;
 -	CHECK_XPATH_NODESET(doc, STR("//section[@role=\"subdivision\"][not(../self::section[@role=\"division\"])]")) % 3;
 -}
 -
 -TEST_XML_FLAGS(xpath_rexml_4, "<a><b number='1' str='abc'>TEXT1</b><c number='1'/><c number='2' str='def'><b number='3'/><d number='1' str='abc'>TEXT2</d><b number='2'><!--COMMENT--></b></c></a>", parse_default | parse_comments)
 -{
 -	CHECK_XPATH_NODESET(doc, STR("/descendant-or-self::node()[count(child::node()|following-sibling::node()|preceding-sibling::node())=0]")) % 6 % 17 % 20;
 -}
 -
 -TEST_XML(xpath_rexml_5, "<a><b><c id='a'/></b><c id='b'/></a>")
 -{
 -	CHECK_XPATH_FAIL(STR(".//[@id]"));
 -	CHECK_XPATH_NODESET(doc, STR(".//self::*[@id]")) % 4 % 6;
 -	CHECK_XPATH_NODESET(doc, STR(".//node()[@id]")) % 4 % 6;
 -}
 -
 -TEST_XML(xpath_rexml_6, "<div><span><strong>a</strong></span><em>b</em></div>")
 -{
 -	CHECK_XPATH_NODESET(doc, STR("//em|//strong")) % 4 % 6;
 -	CHECK_XPATH_NODESET(doc, STR("//*[self::em | self::strong]")) % 4 % 6;
 -	CHECK_XPATH_NODESET(doc, STR("//*[name()=\"em\" or name()=\"strong\"]")) % 4 % 6;
 -	CHECK_XPATH_NODESET(doc, STR("//*[self::em or self::strong]")) % 4 % 6;
 -}
 -
 -TEST_XML(xpath_xsl_list_1, "<input><type>whatever</type></input><input><type>text</type></input><input><type>select</type></input><input><type>something</type></input>")
 -{
 -	// if I'm not last, and the next input/type isn't select
 -	CHECK_XPATH_NODESET(doc, STR("input[type[parent::input/following-sibling::input[1]/type != 'select']]")) % 2 % 8;
 -	CHECK_XPATH_NODESET(doc, STR("input[type[../following-sibling::input[1]/type != 'select']]")) % 2 % 8;
 -
 -	CHECK_XPATH_NODESET(doc, STR("input[position()+1]"));
 -}
 -
 -TEST_XML(xpath_xsl_list_2, "<TR><TD id='1'>text1</TD><TD id='2'>text2</TD><TD id='3'>text3</TD><TD id='4'>text4</TD></TR>")
 -{
 -	CHECK_XPATH_FAIL(STR(".[not(.=ancestor::TR/TD[15]/node())]"));
 -
 -	CHECK_XPATH_NODESET(doc.child(STR("TR")).find_child_by_attribute(STR("TD"), STR("id"), STR("1")).first_child(), STR("self::node()[not(.=ancestor::TR/TD[3]/node())]")) % 5;
 -	CHECK_XPATH_NODESET(doc.child(STR("TR")).find_child_by_attribute(STR("TD"), STR("id"), STR("2")).first_child(), STR("self::node()[not(.=ancestor::TR/TD[3]/node())]")) % 8;
 -	CHECK_XPATH_NODESET(doc.child(STR("TR")).find_child_by_attribute(STR("TD"), STR("id"), STR("3")).first_child(), STR("self::node()[not(.=ancestor::TR/TD[3]/node())]"));
 -	CHECK_XPATH_NODESET(doc.child(STR("TR")).find_child_by_attribute(STR("TD"), STR("id"), STR("4")).first_child(), STR("self::node()[not(.=ancestor::TR/TD[3]/node())]")) % 14;
 -
 -	CHECK_XPATH_NODESET(doc.child(STR("TR")).find_child_by_attribute(STR("TD"), STR("id"), STR("1")), STR("node()[not(.=ancestor::TR/TD[3]/node())]")) % 5;
 -	CHECK_XPATH_NODESET(doc.child(STR("TR")).find_child_by_attribute(STR("TD"), STR("id"), STR("2")), STR("node()[not(.=ancestor::TR/TD[3]/node())]")) % 8;
 -	CHECK_XPATH_NODESET(doc.child(STR("TR")).find_child_by_attribute(STR("TD"), STR("id"), STR("3")), STR("node()[not(.=ancestor::TR/TD[3]/node())]"));
 -	CHECK_XPATH_NODESET(doc.child(STR("TR")).find_child_by_attribute(STR("TD"), STR("id"), STR("4")), STR("node()[not(.=ancestor::TR/TD[3]/node())]")) % 14;
 -}
 -
 -TEST_XML(xpath_star_token, "<node>0.5<section><child/><child/><child/><child/></section><section/></node>")
 -{
 -	CHECK_XPATH_NODESET(doc, STR("//*[/* * 4]")) % 6 % 9;
 -	CHECK_XPATH_NODESET(doc, STR("//*[/**4]")) % 6 % 9;
 -	CHECK_XPATH_FAIL(STR("//*[/***4]"));
 -}
 -
 -TEST(xpath_miscellaneous)
 -{
 -	CHECK_XPATH_FAIL(STR("/root/child[a=3]/substring(child::text())"));
 -	CHECK_XPATH_NODESET(xml_node(), STR("foo/@FOO/@bar"));
 -}
 -
 -#endif
 +#ifndef PUGIXML_NO_XPATH + +#include "common.hpp" + +#include <float.h> +#include <string.h> +#include <wchar.h> + +#include <string> + +TEST(xpath_allocator_many_pages) +{ +	pugi::string_t query = STR("0"); + +	for (int i = 0; i < 128; ++i) query += STR("+string-length('abcdefgh')"); + +	CHECK_XPATH_NUMBER(xml_node(), query.c_str(), 1024); +} + +TEST(xpath_allocator_large_page) +{ +	pugi::string_t query; + +	for (int i = 0; i < 1024; ++i) query += STR("abcdefgh"); + +	CHECK_XPATH_NUMBER(xml_node(), (STR("string-length('") + query + STR("')")).c_str(), 8192); +} + +TEST_XML(xpath_sort_complex, "<node><child1 attr1='value1' attr2='value2'/><child2 attr1='value1'>test</child2></node>") +{ +	// just some random union order, it should not matter probably? +	xpath_node_set ns = doc.child(STR("node")).select_nodes(STR("child1 | child2 | child1/@* | . | child2/@* | child2/text()")); + +	ns.sort(false); +	xpath_node_set sorted = ns; + +	ns.sort(true); +	xpath_node_set reverse_sorted = ns; + +	xpath_node_set_tester(sorted, "sorted order failed") % 2 % 3 % 4 % 5 % 6 % 7 % 8; +	xpath_node_set_tester(reverse_sorted, "reverse sorted order failed") % 8 % 7 % 6 % 5 % 4 % 3 % 2; +} + +TEST_XML(xpath_sort_children, "<node><child><subchild id='1'/></child><child><subchild id='2'/></child></node>") +{ +	xpath_node_set ns = doc.child(STR("node")).select_nodes(STR("child/subchild[@id=1] | child/subchild[@id=2]")); + +	ns.sort(false); +	xpath_node_set sorted = ns; + +	ns.sort(true); +	xpath_node_set reverse_sorted = ns; + +	xpath_node_set_tester(sorted, "sorted order failed") % 4 % 7; +	xpath_node_set_tester(reverse_sorted, "reverse sorted order failed") % 7 % 4; +} + +TEST_XML(xpath_sort_attributes, "<node/>") +{ +	xml_node n = doc.child(STR("node")); + +	// we need to insert attributes manually since unsorted node sets are (always?) sorted via pointers because of remove_duplicates, +	// so we need to have different document and pointer order to cover all comparator cases +	n.append_attribute(STR("attr2")); +	n.append_attribute(STR("attr3")); +	n.insert_attribute_before(STR("attr1"), n.attribute(STR("attr2"))); + +	xpath_node_set ns = n.select_nodes(STR("@*")); + +	ns.sort(true); +	xpath_node_set reverse_sorted = ns; + +	ns.sort(false); +	xpath_node_set sorted = ns; + +	xpath_node_set_tester(sorted, "sorted order failed") % 3 % 4 % 5; +	xpath_node_set_tester(reverse_sorted, "reverse sorted order failed") % 5 % 4 % 3; +} + +TEST(xpath_long_numbers_parse) +{ +	const pugi::char_t* str_flt_max = STR("340282346638528860000000000000000000000"); +	const pugi::char_t* str_flt_max_dec = STR("340282346638528860000000000000000000000.000000"); +	 +	const pugi::char_t* str_dbl_max = STR("179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); +	const pugi::char_t* str_dbl_max_dec = STR("179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000"); + +	xml_node c; + +	// check parsing +	CHECK_XPATH_NUMBER(c, str_flt_max, FLT_MAX); +	CHECK_XPATH_NUMBER(c, str_flt_max_dec, FLT_MAX); +	CHECK_XPATH_NUMBER(c, str_dbl_max, DBL_MAX); +	CHECK_XPATH_NUMBER(c, str_dbl_max_dec, DBL_MAX); +} + +static bool test_xpath_string_prefix(const pugi::xml_node& node, const pugi::char_t* query, const pugi::char_t* expected, size_t match_length) +{ +#ifdef PUGIXML_WCHAR_MODE +	size_t expected_length = wcslen(expected); +#else +	size_t expected_length = strlen(expected); +#endif + +	pugi::xpath_query q(query); +	pugi::string_t value = q.evaluate_string(node); + +	return value.length() == expected_length && value.compare(0, match_length, expected, match_length) == 0; +} + +TEST(xpath_long_numbers_stringize) +{ +	const pugi::char_t* str_flt_max = STR("340282346638528860000000000000000000000"); +	const pugi::char_t* str_flt_max_dec = STR("340282346638528860000000000000000000000.000000"); +	 +	const pugi::char_t* str_dbl_max = STR("179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); +	const pugi::char_t* str_dbl_max_dec = STR("179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000"); + +	xml_node c; + +	CHECK(test_xpath_string_prefix(c, str_flt_max, str_flt_max, 15)); +	CHECK(test_xpath_string_prefix(c, str_flt_max_dec, str_flt_max, 15)); + +	CHECK(test_xpath_string_prefix(c, str_dbl_max, str_dbl_max, 15)); +	CHECK(test_xpath_string_prefix(c, str_dbl_max_dec, str_dbl_max, 15)); +} + +#include <stdio.h> + +TEST(xpath_denorm_numbers) +{ +	pugi::string_t query; + +	// 10^-318 - double denormal +	for (int i = 0; i < 106; ++i) +	{ +		if (i != 0) query += STR(" * "); +		query += STR("0.001"); +	} + +	CHECK_XPATH_STRING(xml_node(), query.c_str(), STR("0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009999987484955998")); +} + +TEST_XML(xpath_rexml_1, "<a><b><c id='a'/></b><c id='b'/></a>") +{ +	CHECK_XPATH_NODESET(doc, STR("//*[local-name()='c' and @id='b']")) % 6; +	CHECK_XPATH_NODESET(doc, STR("//*[ local-name()='c' and @id='b' ]")) % 6; +	CHECK_XPATH_NODESET(doc, STR("/a/c[@id]")) % 6; +	CHECK_XPATH_NODESET(doc, STR("/a/c[(@id)]")) % 6; +	CHECK_XPATH_NODESET(doc, STR("/a/c[ @id ]")) % 6; +	CHECK_XPATH_NODESET(doc, STR("/a/c[ (@id) ]")) % 6; +	CHECK_XPATH_NODESET(doc, STR("/a/c[( @id )]")) % 6; +	CHECK_XPATH_NODESET(doc, STR("/a/c[ ( @id ) ]")) % 6; +	CHECK_XPATH_NODESET(doc, STR("/a/c [ ( @id ) ] ")) % 6; +	CHECK_XPATH_NODESET(doc, STR(" / a / c [ ( @id ) ] ")) % 6; +} + +TEST_XML(xpath_rexml_2, "<a:x xmlns:a='1'><a:y p='p' q='q'><a:z>zzz</a:z></a:y></a:x>") +{ +	CHECK_XPATH_NODESET(doc, STR("a:x/a:y[@p='p' and @q='q']/a:z/text()")) % 8; +} + +TEST_XML(xpath_rexml_3, "<article><section role='subdivision' id='1'><para>free flowing text.</para></section><section role='division'><section role='subdivision' id='2'><para>free flowing text.</para></section><section role='division'><para>free flowing text.</para></section></section></article>") +{ +	CHECK_XPATH_NODESET(doc, STR("//section[../self::section[@role=\"division\"]]")) % 10 % 15; +    CHECK_XPATH_NODESET(doc, STR("//section[@role=\"subdivision\" and not(../self::section[@role=\"division\"])]")) % 3; +	CHECK_XPATH_NODESET(doc, STR("//section[@role=\"subdivision\"][not(../self::section[@role=\"division\"])]")) % 3; +} + +TEST_XML_FLAGS(xpath_rexml_4, "<a><b number='1' str='abc'>TEXT1</b><c number='1'/><c number='2' str='def'><b number='3'/><d number='1' str='abc'>TEXT2</d><b number='2'><!--COMMENT--></b></c></a>", parse_default | parse_comments) +{ +	CHECK_XPATH_NODESET(doc, STR("/descendant-or-self::node()[count(child::node()|following-sibling::node()|preceding-sibling::node())=0]")) % 6 % 17 % 20; +} + +TEST_XML(xpath_rexml_5, "<a><b><c id='a'/></b><c id='b'/></a>") +{ +	CHECK_XPATH_FAIL(STR(".//[@id]")); +	CHECK_XPATH_NODESET(doc, STR(".//self::*[@id]")) % 4 % 6; +	CHECK_XPATH_NODESET(doc, STR(".//node()[@id]")) % 4 % 6; +} + +TEST_XML(xpath_rexml_6, "<div><span><strong>a</strong></span><em>b</em></div>") +{ +	CHECK_XPATH_NODESET(doc, STR("//em|//strong")) % 4 % 6; +	CHECK_XPATH_NODESET(doc, STR("//*[self::em | self::strong]")) % 4 % 6; +	CHECK_XPATH_NODESET(doc, STR("//*[name()=\"em\" or name()=\"strong\"]")) % 4 % 6; +	CHECK_XPATH_NODESET(doc, STR("//*[self::em or self::strong]")) % 4 % 6; +} + +TEST_XML(xpath_xsl_list_1, "<input><type>whatever</type></input><input><type>text</type></input><input><type>select</type></input><input><type>something</type></input>") +{ +	// if I'm not last, and the next input/type isn't select +	CHECK_XPATH_NODESET(doc, STR("input[type[parent::input/following-sibling::input[1]/type != 'select']]")) % 2 % 8; +	CHECK_XPATH_NODESET(doc, STR("input[type[../following-sibling::input[1]/type != 'select']]")) % 2 % 8; + +	CHECK_XPATH_NODESET(doc, STR("input[position()+1]")); +} + +TEST_XML(xpath_xsl_list_2, "<TR><TD id='1'>text1</TD><TD id='2'>text2</TD><TD id='3'>text3</TD><TD id='4'>text4</TD></TR>") +{ +	CHECK_XPATH_FAIL(STR(".[not(.=ancestor::TR/TD[15]/node())]")); + +	CHECK_XPATH_NODESET(doc.child(STR("TR")).find_child_by_attribute(STR("TD"), STR("id"), STR("1")).first_child(), STR("self::node()[not(.=ancestor::TR/TD[3]/node())]")) % 5; +	CHECK_XPATH_NODESET(doc.child(STR("TR")).find_child_by_attribute(STR("TD"), STR("id"), STR("2")).first_child(), STR("self::node()[not(.=ancestor::TR/TD[3]/node())]")) % 8; +	CHECK_XPATH_NODESET(doc.child(STR("TR")).find_child_by_attribute(STR("TD"), STR("id"), STR("3")).first_child(), STR("self::node()[not(.=ancestor::TR/TD[3]/node())]")); +	CHECK_XPATH_NODESET(doc.child(STR("TR")).find_child_by_attribute(STR("TD"), STR("id"), STR("4")).first_child(), STR("self::node()[not(.=ancestor::TR/TD[3]/node())]")) % 14; + +	CHECK_XPATH_NODESET(doc.child(STR("TR")).find_child_by_attribute(STR("TD"), STR("id"), STR("1")), STR("node()[not(.=ancestor::TR/TD[3]/node())]")) % 5; +	CHECK_XPATH_NODESET(doc.child(STR("TR")).find_child_by_attribute(STR("TD"), STR("id"), STR("2")), STR("node()[not(.=ancestor::TR/TD[3]/node())]")) % 8; +	CHECK_XPATH_NODESET(doc.child(STR("TR")).find_child_by_attribute(STR("TD"), STR("id"), STR("3")), STR("node()[not(.=ancestor::TR/TD[3]/node())]")); +	CHECK_XPATH_NODESET(doc.child(STR("TR")).find_child_by_attribute(STR("TD"), STR("id"), STR("4")), STR("node()[not(.=ancestor::TR/TD[3]/node())]")) % 14; +} + +TEST_XML(xpath_star_token, "<node>0.5<section><child/><child/><child/><child/></section><section/></node>") +{ +	CHECK_XPATH_NODESET(doc, STR("//*[/* * 4]")) % 6 % 9; +	CHECK_XPATH_NODESET(doc, STR("//*[/**4]")) % 6 % 9; +	CHECK_XPATH_FAIL(STR("//*[/***4]")); +} + +TEST(xpath_miscellaneous) +{ +	CHECK_XPATH_FAIL(STR("/root/child[a=3]/substring(child::text())")); +	CHECK_XPATH_NODESET(xml_node(), STR("foo/@FOO/@bar")); +} + +#endif diff --git a/tests/test_xpath_api.cpp b/tests/test_xpath_api.cpp index 56e8ff6..f7fc868 100644 --- a/tests/test_xpath_api.cpp +++ b/tests/test_xpath_api.cpp @@ -1,150 +1,150 @@ -#ifndef PUGIXML_NO_XPATH
 -
 -#include "common.hpp"
 -
 -#include "helpers.hpp"
 -
 -#include <string>
 -
 -TEST_XML(xpath_api_select_nodes, "<node><head/><foo/><foo/><tail/></node>")
 -{
 -	xpath_node_set ns1 = doc.select_nodes(STR("node/foo"));
 -
 -	xpath_query q(STR("node/foo"));
 -	xpath_node_set ns2 = doc.select_nodes(q);
 -
 -	xpath_node_set_tester(ns1, "ns1") % 4 % 5;
 -	xpath_node_set_tester(ns2, "ns2") % 4 % 5;
 -}
 -
 -TEST_XML(xpath_api_select_single_node, "<node><head/><foo id='1'/><foo/><tail/></node>")
 -{
 -	xpath_node n1 = doc.select_single_node(STR("node/foo"));
 -
 -	xpath_query q(STR("node/foo"));
 -	xpath_node n2 = doc.select_single_node(q);
 -
 -	CHECK(n1.node().attribute(STR("id")).as_int() == 1);
 -	CHECK(n2.node().attribute(STR("id")).as_int() == 1);
 -
 -	xpath_node n3 = doc.select_single_node(STR("node/bar"));
 -	
 -	CHECK(!n3);
 -
 -	xpath_node n4 = doc.select_single_node(STR("node/head/following-sibling::foo"));
 -	xpath_node n5 = doc.select_single_node(STR("node/tail/preceding-sibling::foo"));
 -	
 -	CHECK(n4.node().attribute(STR("id")).as_int() == 1);
 -	CHECK(n5.node().attribute(STR("id")).as_int() == 1);
 -}
 -
 -TEST(xpath_api_exception_what)
 -{
 -	try
 -	{
 -		xpath_query q(STR(""));
 -	}
 -	catch (const xpath_exception& e)
 -	{
 -		CHECK(e.what()[0] != 0);
 -	}
 -}
 -
 -TEST_XML(xpath_api_node_bool_ops, "<node attr='value'/>")
 -{
 -	generic_bool_ops_test(doc.select_single_node(STR("node")));
 -	generic_bool_ops_test(doc.select_single_node(STR("node/@attr")));
 -}
 -
 -TEST_XML(xpath_api_node_eq_ops, "<node attr='value'/>")
 -{
 -	generic_eq_ops_test(doc.select_single_node(STR("node")), doc.select_single_node(STR("node/@attr")));
 -}
 -
 -TEST_XML(xpath_api_node_accessors, "<node attr='value'/>")
 -{
 -	xpath_node null;
 -	xpath_node node = doc.select_single_node(STR("node"));
 -	xpath_node attr = doc.select_single_node(STR("node/@attr"));
 -
 -	CHECK(!null.node());
 -	CHECK(!null.attribute());
 -	CHECK(!null.parent());
 -
 -	CHECK(node.node() == doc.child(STR("node")));
 -	CHECK(!node.attribute());
 -	CHECK(node.parent() == doc);
 -
 -	CHECK(!attr.node());
 -	CHECK(attr.attribute() == doc.child(STR("node")).attribute(STR("attr")));
 -	CHECK(attr.parent() == doc.child(STR("node")));
 -}
 -
 -inline void xpath_api_node_accessors_helper(const xpath_node_set& set)
 -{
 -	CHECK(set.size() == 2);
 -	CHECK(set.type() == xpath_node_set::type_sorted);
 -	CHECK(!set.empty());
 -	CHECK_STRING(set[0].node().name(), STR("foo"));
 -	CHECK_STRING(set[1].node().name(), STR("foo"));
 -	CHECK(set.first() == set[0]);
 -	CHECK(set.begin() + 2 == set.end());
 -	CHECK(set.begin()[0] == set[0] && set.begin()[1] == set[1]);
 -}
 -
 -TEST_XML(xpath_api_nodeset_accessors, "<node><foo/><foo/></node>")
 -{
 -	xpath_node_set null;
 -	CHECK(null.size() == 0);
 -	CHECK(null.type() == xpath_node_set::type_unsorted);
 -	CHECK(null.empty());
 -	CHECK(!null.first());
 -	CHECK(null.begin() == null.end());
 -
 -	xpath_node_set set = doc.select_nodes(STR("node/foo"));
 -	xpath_api_node_accessors_helper(set);
 -
 -	xpath_node_set copy = set;
 -	xpath_api_node_accessors_helper(copy);
 -
 -	xpath_node_set assigned;
 -	assigned = set;
 -	xpath_api_node_accessors_helper(assigned);
 -
 -	xpath_node_set nullcopy = null;
 -}
 -
 -TEST_XML(xpath_api_evaluate, "<node attr='3'/>")
 -{
 -	xpath_query q(STR("node/@attr"));
 -
 -	CHECK(q.evaluate_boolean(doc));
 -	CHECK(q.evaluate_number(doc) == 3);
 -	CHECK(q.evaluate_string(doc) == STR("3"));
 -
 -	xpath_node_set ns = q.evaluate_node_set(doc);
 -	CHECK(ns.size() == 1 && ns[0].attribute() == doc.child(STR("node")).attribute(STR("attr")));
 -}
 -
 -TEST(xpath_api_evaluate_node_set)
 -{
 -	try
 -	{
 -		xpath_query q(STR("1"));
 -
 -		q.evaluate_node_set(xml_node());
 -	}
 -	catch (const xpath_exception&)
 -	{
 -	}
 -}
 -
 -TEST(xpath_api_return_type)
 -{
 -	CHECK(xpath_query(STR("node")).return_type() == xpath_type_node_set);
 -	CHECK(xpath_query(STR("1")).return_type() == xpath_type_number);
 -	CHECK(xpath_query(STR("'s'")).return_type() == xpath_type_string);
 -	CHECK(xpath_query(STR("true()")).return_type() == xpath_type_boolean);
 -}
 -
 -#endif
 +#ifndef PUGIXML_NO_XPATH + +#include "common.hpp" + +#include "helpers.hpp" + +#include <string> + +TEST_XML(xpath_api_select_nodes, "<node><head/><foo/><foo/><tail/></node>") +{ +	xpath_node_set ns1 = doc.select_nodes(STR("node/foo")); + +	xpath_query q(STR("node/foo")); +	xpath_node_set ns2 = doc.select_nodes(q); + +	xpath_node_set_tester(ns1, "ns1") % 4 % 5; +	xpath_node_set_tester(ns2, "ns2") % 4 % 5; +} + +TEST_XML(xpath_api_select_single_node, "<node><head/><foo id='1'/><foo/><tail/></node>") +{ +	xpath_node n1 = doc.select_single_node(STR("node/foo")); + +	xpath_query q(STR("node/foo")); +	xpath_node n2 = doc.select_single_node(q); + +	CHECK(n1.node().attribute(STR("id")).as_int() == 1); +	CHECK(n2.node().attribute(STR("id")).as_int() == 1); + +	xpath_node n3 = doc.select_single_node(STR("node/bar")); +	 +	CHECK(!n3); + +	xpath_node n4 = doc.select_single_node(STR("node/head/following-sibling::foo")); +	xpath_node n5 = doc.select_single_node(STR("node/tail/preceding-sibling::foo")); +	 +	CHECK(n4.node().attribute(STR("id")).as_int() == 1); +	CHECK(n5.node().attribute(STR("id")).as_int() == 1); +} + +TEST(xpath_api_exception_what) +{ +	try +	{ +		xpath_query q(STR("")); +	} +	catch (const xpath_exception& e) +	{ +		CHECK(e.what()[0] != 0); +	} +} + +TEST_XML(xpath_api_node_bool_ops, "<node attr='value'/>") +{ +	generic_bool_ops_test(doc.select_single_node(STR("node"))); +	generic_bool_ops_test(doc.select_single_node(STR("node/@attr"))); +} + +TEST_XML(xpath_api_node_eq_ops, "<node attr='value'/>") +{ +	generic_eq_ops_test(doc.select_single_node(STR("node")), doc.select_single_node(STR("node/@attr"))); +} + +TEST_XML(xpath_api_node_accessors, "<node attr='value'/>") +{ +	xpath_node null; +	xpath_node node = doc.select_single_node(STR("node")); +	xpath_node attr = doc.select_single_node(STR("node/@attr")); + +	CHECK(!null.node()); +	CHECK(!null.attribute()); +	CHECK(!null.parent()); + +	CHECK(node.node() == doc.child(STR("node"))); +	CHECK(!node.attribute()); +	CHECK(node.parent() == doc); + +	CHECK(!attr.node()); +	CHECK(attr.attribute() == doc.child(STR("node")).attribute(STR("attr"))); +	CHECK(attr.parent() == doc.child(STR("node"))); +} + +inline void xpath_api_node_accessors_helper(const xpath_node_set& set) +{ +	CHECK(set.size() == 2); +	CHECK(set.type() == xpath_node_set::type_sorted); +	CHECK(!set.empty()); +	CHECK_STRING(set[0].node().name(), STR("foo")); +	CHECK_STRING(set[1].node().name(), STR("foo")); +	CHECK(set.first() == set[0]); +	CHECK(set.begin() + 2 == set.end()); +	CHECK(set.begin()[0] == set[0] && set.begin()[1] == set[1]); +} + +TEST_XML(xpath_api_nodeset_accessors, "<node><foo/><foo/></node>") +{ +	xpath_node_set null; +	CHECK(null.size() == 0); +	CHECK(null.type() == xpath_node_set::type_unsorted); +	CHECK(null.empty()); +	CHECK(!null.first()); +	CHECK(null.begin() == null.end()); + +	xpath_node_set set = doc.select_nodes(STR("node/foo")); +	xpath_api_node_accessors_helper(set); + +	xpath_node_set copy = set; +	xpath_api_node_accessors_helper(copy); + +	xpath_node_set assigned; +	assigned = set; +	xpath_api_node_accessors_helper(assigned); + +	xpath_node_set nullcopy = null; +} + +TEST_XML(xpath_api_evaluate, "<node attr='3'/>") +{ +	xpath_query q(STR("node/@attr")); + +	CHECK(q.evaluate_boolean(doc)); +	CHECK(q.evaluate_number(doc) == 3); +	CHECK(q.evaluate_string(doc) == STR("3")); + +	xpath_node_set ns = q.evaluate_node_set(doc); +	CHECK(ns.size() == 1 && ns[0].attribute() == doc.child(STR("node")).attribute(STR("attr"))); +} + +TEST(xpath_api_evaluate_node_set) +{ +	try +	{ +		xpath_query q(STR("1")); + +		q.evaluate_node_set(xml_node()); +	} +	catch (const xpath_exception&) +	{ +	} +} + +TEST(xpath_api_return_type) +{ +	CHECK(xpath_query(STR("node")).return_type() == xpath_type_node_set); +	CHECK(xpath_query(STR("1")).return_type() == xpath_type_number); +	CHECK(xpath_query(STR("'s'")).return_type() == xpath_type_string); +	CHECK(xpath_query(STR("true()")).return_type() == xpath_type_boolean); +} + +#endif diff --git a/tests/test_xpath_functions.cpp b/tests/test_xpath_functions.cpp index 7b44294..3eb69c6 100644 --- a/tests/test_xpath_functions.cpp +++ b/tests/test_xpath_functions.cpp @@ -1,747 +1,747 @@ -#ifndef PUGIXML_NO_XPATH
 -
 -#include "common.hpp"
 -
 -TEST_XML(xpath_number_number, "<node>123</node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node")).first_child();
 -	
 -	// number with 0 arguments
 -	CHECK_XPATH_NUMBER_NAN(c, STR("number()"));
 -	CHECK_XPATH_NUMBER(n, STR("number()"), 123);
 -
 -	// number with 1 string argument
 -	CHECK_XPATH_NUMBER(c, STR("number(' -123.456 ')"), -123.456);
 -	CHECK_XPATH_NUMBER(c, STR("number(' -123.')"), -123);
 -	CHECK_XPATH_NUMBER(c, STR("number('123.')"), 123);
 -	CHECK_XPATH_NUMBER(c, STR("number('.56')"), 0.56);
 -	CHECK_XPATH_NUMBER(c, STR("number('123 ')"), 123);
 -	CHECK_XPATH_NUMBER_NAN(c, STR("number('foobar')"));
 -	CHECK_XPATH_NUMBER_NAN(c, STR("number('f1')"));
 -	CHECK_XPATH_NUMBER_NAN(c, STR("number('1f')"));
 -	CHECK_XPATH_NUMBER_NAN(c, STR("number('1.f')"));
 -	CHECK_XPATH_NUMBER_NAN(c, STR("number('1.0f')"));
 -	CHECK_XPATH_NUMBER_NAN(c, STR("number('123 f')"));
 -	CHECK_XPATH_NUMBER_NAN(c, STR("number('')"));
 -	CHECK_XPATH_NUMBER_NAN(c, STR("number('.')"));
 -
 -	// number with 1 bool argument
 -	CHECK_XPATH_NUMBER(c, STR("number(true())"), 1);
 -	CHECK_XPATH_NUMBER(c, STR("number(false())"), 0);
 -
 -	// number with 1 node set argument
 -	CHECK_XPATH_NUMBER(n, STR("number(.)"), 123);
 -
 -	// number with 1 number argument
 -	CHECK_XPATH_NUMBER(c, STR("number(1)"), 1);
 -	
 -	// number with 2 arguments
 -	CHECK_XPATH_FAIL(STR("number(1, 2)"));
 -}
 -
 -TEST_XML(xpath_number_sum, "<node>123<child>789</child></node><node/>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -	
 -	// sum with 0 arguments
 -	CHECK_XPATH_FAIL(STR("sum()"));
 -
 -	// sum with 1 argument
 -	CHECK_XPATH_NUMBER(c, STR("sum(.)"), 0);
 -	CHECK_XPATH_NUMBER(n, STR("sum(.)"), 123789); // 123 .. 789
 -	
 -	CHECK_XPATH_NUMBER(n, STR("sum(./descendant-or-self::node())"), 125490); // node + 123 + child + 789 = 123789 + 123 + 789 + 789 = 125490
 -	CHECK_XPATH_NUMBER(n, STR("sum(.//node())"), 1701); // 123 + child + 789 = 123 + 789 + 789
 -	CHECK_XPATH_NUMBER_NAN(doc.last_child(), STR("sum(.)"));
 -
 -	// sum with 2 arguments
 -	CHECK_XPATH_FAIL(STR("sum(1, 2)"));
 -	
 -	// sum with 1 non-node-set argument
 -	CHECK_XPATH_FAIL(STR("sum(1)"));
 -}
 -
 -TEST(xpath_number_floor)
 -{
 -	xml_node c;
 -
 -	// floor with 0 arguments
 -	CHECK_XPATH_FAIL(STR("floor()"));
 -
 -	// floor with 1 argument
 -	CHECK_XPATH_NUMBER(c, STR("floor(0)"), 0);
 -	CHECK_XPATH_NUMBER(c, STR("floor(1.2)"), 1);
 -	CHECK_XPATH_NUMBER(c, STR("floor(1)"), 1);
 -	CHECK_XPATH_NUMBER(c, STR("floor(-1.2)"), -2);
 -	CHECK_XPATH_NUMBER_NAN(c, STR("floor(string('nan'))"));
 -	CHECK_XPATH_STRING(c, STR("string(floor(1 div 0))"), STR("Infinity"));
 -	CHECK_XPATH_STRING(c, STR("string(floor(-1 div 0))"), STR("-Infinity"));
 -
 -	// floor with 2 arguments
 -	CHECK_XPATH_FAIL(STR("floor(1, 2)"));
 -
 -	// floor with argument 0 should return 0
 -	CHECK_XPATH_STRING(c, STR("string(1 div floor(0))"), STR("Infinity"));
 -
 -	// floor with argument -0 should return -0
 -#if !(defined(__APPLE__) && defined(__MACH__)) // MacOS X gcc 4.0.1 implements floor incorrectly (floor never returns -0)
 -	CHECK_XPATH_STRING(c, STR("string(1 div floor(-0))"), STR("-Infinity"));
 -#endif
 -}
 -
 -TEST(xpath_number_ceiling)
 -{
 -	xml_node c;
 -
 -	// ceiling with 0 arguments
 -	CHECK_XPATH_FAIL(STR("ceiling()"));
 -
 -	// ceiling with 1 argument
 -	CHECK_XPATH_NUMBER(c, STR("ceiling(0)"), 0);
 -	CHECK_XPATH_NUMBER(c, STR("ceiling(1.2)"), 2);
 -	CHECK_XPATH_NUMBER(c, STR("ceiling(1)"), 1);
 -	CHECK_XPATH_NUMBER(c, STR("ceiling(-1.2)"), -1);
 -	CHECK_XPATH_NUMBER_NAN(c, STR("ceiling(string('nan'))"));
 -	CHECK_XPATH_STRING(c, STR("string(ceiling(1 div 0))"), STR("Infinity"));
 -	CHECK_XPATH_STRING(c, STR("string(ceiling(-1 div 0))"), STR("-Infinity"));
 -
 -	// ceiling with 2 arguments
 -	CHECK_XPATH_FAIL(STR("ceiling(1, 2)"));
 -
 -	// ceiling with argument 0 should return 0
 -	CHECK_XPATH_STRING(c, STR("string(1 div ceiling(0))"), STR("Infinity"));
 -
 -	// ceiling with argument in range (-1, -0] should result in minus zero
 -#if !(defined(__APPLE__) && defined(__MACH__)) // MacOS X gcc 4.0.1 implements ceil incorrectly (ceil never returns -0)
 -	CHECK_XPATH_STRING(c, STR("string(1 div ceiling(-0))"), STR("-Infinity"));
 -	CHECK_XPATH_STRING(c, STR("string(1 div ceiling(-0.1))"), STR("-Infinity"));
 -#endif
 -}
 -
 -TEST(xpath_number_round)
 -{
 -	xml_node c;
 -
 -	// round with 0 arguments
 -	CHECK_XPATH_FAIL(STR("round()"));
 -
 -	// round with 1 argument
 -	CHECK_XPATH_NUMBER(c, STR("round(1.2)"), 1);
 -	CHECK_XPATH_NUMBER(c, STR("round(1.5)"), 2);
 -	CHECK_XPATH_NUMBER(c, STR("round(1.8)"), 2);
 -	CHECK_XPATH_NUMBER(c, STR("round(1)"), 1);
 -	CHECK_XPATH_NUMBER(c, STR("round(-1.2)"), -1);
 -	CHECK_XPATH_NUMBER(c, STR("round(-1.5)"), -1);
 -	CHECK_XPATH_NUMBER(c, STR("round(-1.6)"), -2);
 -	CHECK_XPATH_NUMBER_NAN(c, STR("round(string('nan'))"));
 -	CHECK_XPATH_STRING(c, STR("string(round(1 div 0))"), STR("Infinity"));
 -	CHECK_XPATH_STRING(c, STR("string(round(-1 div 0))"), STR("-Infinity"));
 -
 -	// round with 2 arguments
 -	CHECK_XPATH_FAIL(STR("round(1, 2)"));
 -
 -	// round with argument in range [-0.5, -0] should result in minus zero
 -	CHECK_XPATH_STRING(c, STR("string(1 div round(0))"), STR("Infinity"));
 -
 -#if !(defined(__APPLE__) && defined(__MACH__)) // MacOS X gcc 4.0.1 implements ceil incorrectly (ceil never returns -0)
 -	CHECK_XPATH_STRING(c, STR("string(1 div round(-0.5))"), STR("-Infinity"));
 -	CHECK_XPATH_STRING(c, STR("string(1 div round(-0))"), STR("-Infinity"));
 -	CHECK_XPATH_STRING(c, STR("string(1 div round(-0.1))"), STR("-Infinity"));
 -#endif
 -}
 -
 -TEST_XML(xpath_boolean_boolean, "<node />")
 -{
 -	xml_node c;
 -	
 -	// boolean with 0 arguments
 -	CHECK_XPATH_FAIL(STR("boolean()"));
 -
 -	// boolean with 1 number argument
 -	CHECK_XPATH_BOOLEAN(c, STR("boolean(0)"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("boolean(1)"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("boolean(-1)"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("boolean(0.1)"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("boolean(number('nan'))"), false);
 -
 -	// boolean with 1 string argument
 -	CHECK_XPATH_BOOLEAN(c, STR("boolean('x')"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("boolean('')"), false);
 -
 -	// boolean with 1 node set argument
 -	CHECK_XPATH_BOOLEAN(c, STR("boolean(.)"), false);
 -	CHECK_XPATH_BOOLEAN(doc, STR("boolean(.)"), true);
 -	CHECK_XPATH_BOOLEAN(doc, STR("boolean(foo)"), false);
 -
 -	// boolean with 2 arguments
 -	CHECK_XPATH_FAIL(STR("boolean(1, 2)"));
 -}
 -
 -TEST(xpath_boolean_not)
 -{
 -	xml_node c;
 -	
 -	// not with 0 arguments
 -	CHECK_XPATH_FAIL(STR("not()"));
 -
 -	// not with 1 argument
 -	CHECK_XPATH_BOOLEAN(c, STR("not(true())"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("not(false())"), true);
 -	
 -	// boolean with 2 arguments
 -	CHECK_XPATH_FAIL(STR("not(1, 2)"));
 -}
 -
 -TEST(xpath_boolean_true)
 -{
 -	xml_node c;
 -	
 -	// true with 0 arguments
 -	CHECK_XPATH_BOOLEAN(c, STR("true()"), true);
 -
 -	// true with 1 argument
 -	CHECK_XPATH_FAIL(STR("true(1)"));
 -}
 -
 -TEST(xpath_boolean_false)
 -{
 -	xml_node c;
 -	
 -	// false with 0 arguments
 -	CHECK_XPATH_BOOLEAN(c, STR("false()"), false);
 -
 -	// false with 1 argument
 -	CHECK_XPATH_FAIL(STR("false(1)"));
 -}
 -
 -TEST_XML(xpath_boolean_lang, "<node xml:lang='en'><child xml:lang='ru-UK'><subchild/></child></node><foo><bar/></foo>")
 -{
 -	xml_node c;
 -	
 -	// lang with 0 arguments
 -	CHECK_XPATH_FAIL(STR("lang()"));
 -
 -	// lang with 1 argument, no language
 -	CHECK_XPATH_BOOLEAN(c, STR("lang('en')"), false);
 -	CHECK_XPATH_BOOLEAN(doc.child(STR("foo")), STR("lang('en')"), false);
 -	CHECK_XPATH_BOOLEAN(doc.child(STR("foo")), STR("lang('')"), false);
 -	CHECK_XPATH_BOOLEAN(doc.child(STR("foo")).child(STR("bar")), STR("lang('en')"), false);
 -	
 -	// lang with 1 argument, same language/prefix
 -	CHECK_XPATH_BOOLEAN(doc.child(STR("node")), STR("lang('en')"), true);
 -	CHECK_XPATH_BOOLEAN(doc.child(STR("node")).child(STR("child")), STR("lang('ru-uk')"), true);
 -	CHECK_XPATH_BOOLEAN(doc.child(STR("node")).child(STR("child")), STR("lang('ru')"), true);
 -	CHECK_XPATH_BOOLEAN(doc.child(STR("node")).child(STR("child")).child(STR("subchild")), STR("lang('ru')"), true);
 -	CHECK_XPATH_BOOLEAN(doc.child(STR("node")).child(STR("child")).child(STR("subchild")), STR("lang('RU')"), true);
 -
 -	// lang with 1 argument, different language/prefix
 -	CHECK_XPATH_BOOLEAN(doc.child(STR("node")), STR("lang('')"), false);
 -	CHECK_XPATH_BOOLEAN(doc.child(STR("node")), STR("lang('e')"), false);
 -	CHECK_XPATH_BOOLEAN(doc.child(STR("node")).child(STR("child")), STR("lang('en')"), false);
 -	CHECK_XPATH_BOOLEAN(doc.child(STR("node")).child(STR("child")), STR("lang('ru-gb')"), false);
 -	CHECK_XPATH_BOOLEAN(doc.child(STR("node")).child(STR("child")), STR("lang('r')"), false);
 -	CHECK_XPATH_BOOLEAN(doc.child(STR("node")).child(STR("child")).child(STR("subchild")), STR("lang('en')"), false);
 -
 -	// lang with 2 arguments
 -	CHECK_XPATH_FAIL(STR("lang(1, 2)"));
 -}
 -
 -TEST_XML(xpath_string_string, "<node>123<child id='1'>789</child><child><![CDATA[200]]></child>100</node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	// string with 0 arguments
 -	CHECK_XPATH_STRING(c, STR("string()"), STR(""));
 -	CHECK_XPATH_STRING(n.child(STR("child")), STR("string()"), STR("789"));
 -
 -	// string with 1 node-set argument
 -	CHECK_XPATH_STRING(n, STR("string(child)"), STR("789"));
 -	CHECK_XPATH_STRING(n, STR("string(child/@id)"), STR("1"));
 -	CHECK_XPATH_STRING(n, STR("string(.)"), STR("123789200100"));
 -
 -	// string with 1 number argument
 -	CHECK_XPATH_STRING(c, STR("string(0 div 0)"), STR("NaN"));
 -	CHECK_XPATH_STRING(c, STR("string(0)"), STR("0"));
 -	CHECK_XPATH_STRING(c, STR("string(-0)"), STR("0"));
 -	CHECK_XPATH_STRING(c, STR("string(1 div 0)"), STR("Infinity"));
 -	CHECK_XPATH_STRING(c, STR("string(-1 div -0)"), STR("Infinity"));
 -	CHECK_XPATH_STRING(c, STR("string(-1 div 0)"), STR("-Infinity"));
 -	CHECK_XPATH_STRING(c, STR("string(1 div -0)"), STR("-Infinity"));
 -	CHECK_XPATH_STRING(c, STR("string(1234567)"), STR("1234567"));
 -	CHECK_XPATH_STRING(c, STR("string(-1234567)"), STR("-1234567"));
 -	CHECK_XPATH_STRING(c, STR("string(1234.5678)"), STR("1234.5678"));
 -	CHECK_XPATH_STRING(c, STR("string(-1234.5678)"), STR("-1234.5678"));
 -	CHECK_XPATH_STRING(c, STR("string(0.5678)"), STR("0.5678"));
 -	CHECK_XPATH_STRING(c, STR("string(-0.5678)"), STR("-0.5678"));
 -	CHECK_XPATH_STRING(c, STR("string(0.0)"), STR("0"));
 -	CHECK_XPATH_STRING(c, STR("string(-0.0)"), STR("0"));
 -
 -	// string with 1 boolean argument
 -	CHECK_XPATH_STRING(c, STR("string(true())"), STR("true"));
 -	CHECK_XPATH_STRING(c, STR("string(false())"), STR("false"));
 -
 -	// string with 1 string argument
 -	CHECK_XPATH_STRING(c, STR("string('abc')"), STR("abc"));
 -
 -	// string with 2 arguments
 -	CHECK_XPATH_FAIL(STR("string(1, 2)"));
 -}
 -
 -TEST(xpath_string_concat)
 -{
 -	xml_node c;
 -
 -	// concat with 0 arguments
 -	CHECK_XPATH_FAIL(STR("concat()"));
 -
 -	// concat with 1 argument
 -	CHECK_XPATH_FAIL(STR("concat('')"));
 -
 -	// concat with exactly 2 arguments
 -	CHECK_XPATH_STRING(c, STR("concat('prev','next')"), STR("prevnext"));
 -	CHECK_XPATH_STRING(c, STR("concat('','next')"), STR("next"));
 -	CHECK_XPATH_STRING(c, STR("concat('prev','')"), STR("prev"));
 -
 -	// concat with 3 or more arguments
 -	CHECK_XPATH_STRING(c, STR("concat('a', 'b', 'c')"), STR("abc"));
 -	CHECK_XPATH_STRING(c, STR("concat('a', 'b', 'c', 'd')"), STR("abcd"));
 -	CHECK_XPATH_STRING(c, STR("concat('a', 'b', 'c', 'd', 'e')"), STR("abcde"));
 -	CHECK_XPATH_STRING(c, STR("concat('a', 'b', 'c', 'd', 'e', 'f')"), STR("abcdef"));
 -	CHECK_XPATH_STRING(c, STR("concat('a', 'b', 'c', 'd', 'e', 'f', 'g')"), STR("abcdefg"));
 -	CHECK_XPATH_STRING(c, STR("concat(1, 2, 3, 4, 5, 6, 7, 8)"), STR("12345678"));
 -}
 -
 -TEST(xpath_string_starts_with)
 -{
 -	xml_node c;
 -
 -	// starts-with with 0 arguments
 -	CHECK_XPATH_FAIL(STR("starts-with()"));
 -
 -	// starts-with with 1 argument
 -	CHECK_XPATH_FAIL(STR("starts-with('a')"));
 -
 -	// starts-with with 2 arguments
 -	CHECK_XPATH_BOOLEAN(c, STR("starts-with('abc', '')"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("starts-with('abc', 'a')"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("starts-with('abc', 'abc')"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("starts-with('abc', 'abcd')"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("starts-with('bc', 'c')"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("starts-with('', 'c')"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("starts-with('', '')"), true);
 -
 -	// starts-with with 3 arguments
 -	CHECK_XPATH_FAIL(STR("starts-with('a', 'b', 'c')"));
 -}
 -
 -TEST(xpath_string_contains)
 -{
 -	xml_node c;
 -
 -	// contains with 0 arguments
 -	CHECK_XPATH_FAIL(STR("contains()"));
 -
 -	// contains with 1 argument
 -	CHECK_XPATH_FAIL(STR("contains('a')"));
 -
 -	// contains with 2 arguments
 -	CHECK_XPATH_BOOLEAN(c, STR("contains('abc', '')"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("contains('abc', 'a')"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("contains('abc', 'abc')"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("contains('abcd', 'bc')"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("contains('abc', 'abcd')"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("contains('b', 'bc')"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("contains('', 'c')"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("contains('', '')"), true);
 -
 -	// contains with 3 arguments
 -	CHECK_XPATH_FAIL(STR("contains('a', 'b', 'c')"));
 -}
 -
 -TEST(xpath_string_substring_before)
 -{
 -	xml_node c;
 -
 -	// substring-before with 0 arguments
 -	CHECK_XPATH_FAIL(STR("substring-before()"));
 -
 -	// substring-before with 1 argument
 -	CHECK_XPATH_FAIL(STR("substring-before('a')"));
 -	
 -	// substring-before with 2 arguments
 -	CHECK_XPATH_STRING(c, STR("substring-before('abc', 'abc')"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("substring-before('abc', 'a')"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("substring-before('abc', 'cd')"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("substring-before('abc', 'b')"), STR("a"));
 -	CHECK_XPATH_STRING(c, STR("substring-before('abc', 'c')"), STR("ab"));
 -	CHECK_XPATH_STRING(c, STR("substring-before('', '')"), STR(""));
 -	
 -	// substring-before with 2 arguments, from W3C standard
 -	CHECK_XPATH_STRING(c, STR("substring-before(\"1999/04/01\",\"/\")"), STR("1999"));
 -
 -	// substring-before with 3 arguments
 -	CHECK_XPATH_FAIL(STR("substring-before('a', 'b', 'c')"));
 -}
 -
 -TEST(xpath_string_substring_after)
 -{
 -	xml_node c;
 -
 -	// substring-after with 0 arguments
 -	CHECK_XPATH_FAIL(STR("substring-after()"));
 -
 -	// substring-after with 1 argument
 -	CHECK_XPATH_FAIL(STR("substring-after('a')"));
 -	
 -	// substring-after with 2 arguments
 -	CHECK_XPATH_STRING(c, STR("substring-after('abc', 'abc')"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("substring-after('abc', 'a')"), STR("bc"));
 -	CHECK_XPATH_STRING(c, STR("substring-after('abc', 'cd')"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("substring-after('abc', 'b')"), STR("c"));
 -	CHECK_XPATH_STRING(c, STR("substring-after('abc', 'c')"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("substring-after('', '')"), STR(""));
 -
 -	// substring-before with 2 arguments, from W3C standard
 -	CHECK_XPATH_STRING(c, STR("substring-after(\"1999/04/01\",\"/\")"), STR("04/01"));
 -	CHECK_XPATH_STRING(c, STR("substring-after(\"1999/04/01\",\"19\")"), STR("99/04/01"));
 -
 -	// substring-after with 3 arguments
 -	CHECK_XPATH_FAIL(STR("substring-after('a', 'b', 'c')"));
 -}
 -
 -TEST(xpath_string_substring)
 -{
 -	xml_node c;
 -
 -	// substring with 0 arguments
 -	CHECK_XPATH_FAIL(STR("substring()"));
 -	
 -	// substring with 1 argument
 -	CHECK_XPATH_FAIL(STR("substring('')"));
 -	
 -	// substring with 2 arguments
 -	CHECK_XPATH_STRING(c, STR("substring('abcd', 2)"), STR("bcd"));
 -	CHECK_XPATH_STRING(c, STR("substring('abcd', 1)"), STR("abcd"));
 -	CHECK_XPATH_STRING(c, STR("substring('abcd', 1.1)"), STR("abcd"));
 -	CHECK_XPATH_STRING(c, STR("substring('abcd', 1.5)"), STR("bcd"));
 -	CHECK_XPATH_STRING(c, STR("substring('abcd', 1.8)"), STR("bcd"));
 -	CHECK_XPATH_STRING(c, STR("substring('abcd', 10)"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("substring('abcd', 0)"), STR("abcd"));
 -	CHECK_XPATH_STRING(c, STR("substring('abcd', -100)"), STR("abcd"));
 -	CHECK_XPATH_STRING(c, STR("substring('abcd', -1 div 0)"), STR("abcd"));
 -	CHECK_XPATH_STRING(c, STR("substring('abcd', 1 div 0)"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("substring('abcd', 0 div 0)"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("substring('', 1)"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("substring('', 0)"), STR(""));
 -    CHECK_XPATH_STRING(c, STR("substring(substring('internalexternalcorrect substring',9),9)"), STR("correct substring"));
 -
 -	// substring with 3 arguments
 -	CHECK_XPATH_STRING(c, STR("substring('abcd', 2, 1)"), STR("b"));
 -	CHECK_XPATH_STRING(c, STR("substring('abcd', 2, 2)"), STR("bc"));
 -	CHECK_XPATH_STRING(c, STR("substring('abcd', 1, 0)"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("substring('abcd', 1, 0.4)"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("substring('abcd', 1, 0.5)"), STR("a"));
 -	CHECK_XPATH_STRING(c, STR("substring('abcd', 10, -5)"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("substring('abcd', 0, -1)"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("substring('abcd', -100, 100)"), STR("abcd"));
 -	CHECK_XPATH_STRING(c, STR("substring('abcd', -1 div 0, 4)"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("substring('abcd', 1 div 0, 0 div 0)"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("substring('abcd', 0 div 0, 1)"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("substring('', 1, 2)"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("substring('', 0, 0)"), STR(""));
 -
 -	// substring with 3 arguments, from W3C standard
 -	CHECK_XPATH_STRING(c, STR("substring('12345', 1.5, 2.6)"), STR("234"));
 -	CHECK_XPATH_STRING(c, STR("substring('12345', 0, 3)"), STR("12"));
 -	CHECK_XPATH_STRING(c, STR("substring('12345', 0 div 0, 3)"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("substring('12345', 1, 0 div 0)"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("substring('12345', -42, 1 div 0)"), STR("12345"));
 -	CHECK_XPATH_STRING(c, STR("substring('12345', -1 div 0, 1 div 0)"), STR(""));
 -
 -	// substring with 4 arguments
 -	CHECK_XPATH_FAIL(STR("substring('', 1, 2, 3)"));
 -}
 -
 -TEST_XML(xpath_string_string_length, "<node>123</node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	// string-length with 0 arguments
 -	CHECK_XPATH_NUMBER(c, STR("string-length()"), 0);
 -	CHECK_XPATH_NUMBER(n, STR("string-length()"), 3);
 -
 -	// string-length with 1 argument
 -	CHECK_XPATH_NUMBER(c, STR("string-length('')"), 0);
 -	CHECK_XPATH_NUMBER(c, STR("string-length('a')"), 1);
 -	CHECK_XPATH_NUMBER(c, STR("string-length('abcdef')"), 6);
 -
 -	// string-length with 2 arguments
 -	CHECK_XPATH_FAIL(STR("string-length(1, 2)"));
 -}
 -
 -TEST_XML_FLAGS(xpath_string_normalize_space, "<node> \t\r\rval1  \rval2\r\nval3\nval4\r\r</node>", parse_minimal)
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	// normalize-space with 0 arguments
 -	CHECK_XPATH_STRING(c, STR("normalize-space()"), STR(""));
 -	CHECK_XPATH_STRING(n, STR("normalize-space()"), STR("val1 val2 val3 val4"));
 -	
 -	// normalize-space with 1 argument
 -	CHECK_XPATH_STRING(c, STR("normalize-space('')"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("normalize-space('abcd')"), STR("abcd"));
 -	CHECK_XPATH_STRING(c, STR("normalize-space(' \r\nabcd')"), STR("abcd"));
 -	CHECK_XPATH_STRING(c, STR("normalize-space('abcd \n\r')"), STR("abcd"));
 -	CHECK_XPATH_STRING(c, STR("normalize-space('ab\r\n\tcd')"), STR("ab cd"));
 -	CHECK_XPATH_STRING(c, STR("normalize-space('ab    cd')"), STR("ab cd"));
 -	CHECK_XPATH_STRING(c, STR("normalize-space('\07')"), STR("\07"));
 -	
 -	// normalize-space with 2 arguments
 -	CHECK_XPATH_FAIL(STR("normalize-space(1, 2)"));
 -}
 -
 -TEST(xpath_string_translate)
 -{
 -	xml_node c;
 -
 -	// translate with 0 arguments
 -	CHECK_XPATH_FAIL(STR("translate()"));
 -	
 -	// translate with 1 argument
 -	CHECK_XPATH_FAIL(STR("translate('a')"));
 -
 -	// translate with 2 arguments
 -	CHECK_XPATH_FAIL(STR("translate('a', 'b')"));
 -	
 -	// translate with 3 arguments
 -	CHECK_XPATH_STRING(c, STR("translate('abc', '', '')"), STR("abc"));
 -	CHECK_XPATH_STRING(c, STR("translate('abc', '', 'foo')"), STR("abc"));
 -	CHECK_XPATH_STRING(c, STR("translate('abc', 'ab', 'ba')"), STR("bac"));
 -	CHECK_XPATH_STRING(c, STR("translate('abc', 'ab', 'f')"), STR("fc"));
 -	CHECK_XPATH_STRING(c, STR("translate('abc', 'aabb', '1234')"), STR("13c"));
 -	CHECK_XPATH_STRING(c, STR("translate('', 'abc', 'bac')"), STR(""));
 -
 -	// translate with 3 arguments, from W3C standard
 -	CHECK_XPATH_STRING(c, STR("translate('bar','abc','ABC')"), STR("BAr"));
 -	CHECK_XPATH_STRING(c, STR("translate('--aaa--','abc-','ABC')"), STR("AAA"));
 -
 -	// translate with 4 arguments
 -	CHECK_XPATH_FAIL(STR("translate('a', 'b', 'c', 'd')"));
 -}
 -
 -TEST_XML(xpath_nodeset_last, "<node><c1/><c1/><c2/><c3/><c3/><c3/><c3/></node>")
 -{
 -	xml_node n = doc.child(STR("node"));
 -
 -	// last with 0 arguments
 -	CHECK_XPATH_NUMBER(n, STR("last()"), 1);
 -	CHECK_XPATH_NODESET(n, STR("c1[last() = 1]"));
 -	CHECK_XPATH_NODESET(n, STR("c1[last() = 2]")) % 3 % 4; // c1, c1
 -	CHECK_XPATH_NODESET(n, STR("c2/preceding-sibling::node()[last() = 2]")) % 4 % 3; // c1, c1
 -
 -	// last with 1 argument
 -	CHECK_XPATH_FAIL(STR("last(c)"));
 -}
 -
 -TEST_XML(xpath_nodeset_position, "<node><c1/><c1/><c2/><c3/><c3/><c3/><c3/></node>")
 -{
 -	xml_node n = doc.child(STR("node"));
 -
 -	// position with 0 arguments
 -	CHECK_XPATH_NUMBER(n, STR("position()"), 1);
 -	CHECK_XPATH_NODESET(n, STR("c1[position() = 0]"));
 -	CHECK_XPATH_NODESET(n, STR("c1[position() = 1]")) % 3;
 -	CHECK_XPATH_NODESET(n, STR("c1[position() = 2]")) % 4;
 -	CHECK_XPATH_NODESET(n, STR("c1[position() = 3]"));
 -	CHECK_XPATH_NODESET(n, STR("c2/preceding-sibling::node()[position() = 1]")) % 4;
 -	CHECK_XPATH_NODESET(n, STR("c2/preceding-sibling::node()[position() = 2]")) % 3;
 -	
 -	// position with 1 argument
 -	CHECK_XPATH_FAIL(STR("position(c)"));
 -}
 -
 -TEST_XML(xpath_nodeset_count, "<node><c1/><c1/><c2/><c3/><c3/><c3/><c3/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	// count with 0 arguments
 -	CHECK_XPATH_FAIL(STR("count()"));
 -
 -	// count with 1 non-node-set argument
 -	CHECK_XPATH_FAIL(STR("count(1)"));
 -	CHECK_XPATH_FAIL(STR("count(true())"));
 -	CHECK_XPATH_FAIL(STR("count('')"));
 -
 -	// count with 1 node-set argument
 -	CHECK_XPATH_NUMBER(c, STR("count(.)"), 0);
 -	CHECK_XPATH_NUMBER(n, STR("count(.)"), 1);
 -	CHECK_XPATH_NUMBER(n, STR("count(c1)"), 2);
 -	CHECK_XPATH_NUMBER(n, STR("count(c2)"), 1);
 -	CHECK_XPATH_NUMBER(n, STR("count(c3)"), 4);
 -	CHECK_XPATH_NUMBER(n, STR("count(c4)"), 0);
 -
 -	// count with 2 arguments
 -	CHECK_XPATH_FAIL(STR("count(x, y)"));
 -}
 -
 -TEST_XML(xpath_nodeset_id, "<node id='foo'/>")
 -{
 -	xml_node n = doc.child(STR("node"));
 -
 -	// id with 0 arguments
 -	CHECK_XPATH_FAIL(STR("id()"));
 -	
 -	// id with 1 argument - no DTD => no id
 -	CHECK_XPATH_NODESET(n, STR("id('foo')"));
 -
 -	// id with 2 arguments
 -	CHECK_XPATH_FAIL(STR("id(1, 2)"));
 -}
 -
 -TEST_XML_FLAGS(xpath_nodeset_local_name, "<node xmlns:foo='http://foo'><c1>text</c1><c2 xmlns:foo='http://foo2' foo:attr='value'><foo:child/></c2><c3 xmlns='http://def' attr='value'><child/></c3><c4><?target stuff?></c4></node>", parse_default | parse_pi)
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	// local-name with 0 arguments
 -	CHECK_XPATH_STRING(c, STR("local-name()"), STR(""));
 -	CHECK_XPATH_STRING(n, STR("local-name()"), STR("node"));
 -	
 -	// local-name with 1 non-node-set argument
 -	CHECK_XPATH_FAIL(STR("local-name(1)"));
 -
 -	// local-name with 1 node-set argument
 -	CHECK_XPATH_STRING(n, STR("local-name(c1)"), STR("c1"));
 -	CHECK_XPATH_STRING(n, STR("local-name(c2/node())"), STR("child"));
 -	CHECK_XPATH_STRING(n, STR("local-name(c2/attribute::node())"), STR("attr"));
 -	CHECK_XPATH_STRING(n, STR("local-name(c1/node())"), STR(""));
 -	CHECK_XPATH_STRING(n, STR("local-name(c4/node())"), STR("target"));
 -	CHECK_XPATH_STRING(n, STR("local-name(c1/following-sibling::node())"), STR("c2"));
 -	CHECK_XPATH_STRING(n, STR("local-name(c4/preceding-sibling::node())"), STR("c1"));
 -
 -	// local-name with 2 arguments
 -	CHECK_XPATH_FAIL(STR("local-name(c1, c2)"));
 -}
 -
 -TEST_XML_FLAGS(xpath_nodeset_namespace_uri, "<node xmlns:foo='http://foo'><c1>text</c1><c2 xmlns:foo='http://foo2' foo:attr='value'><foo:child/></c2><c3 xmlns='http://def' attr='value'><child/></c3><c4><?target stuff?></c4><c5><foo:child/></c5><c6 bar:attr=''/></node>", parse_default | parse_pi)
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	// namespace-uri with 0 arguments
 -	CHECK_XPATH_STRING(c, STR("namespace-uri()"), STR(""));
 -	CHECK_XPATH_STRING(n.child(STR("c2")).child(STR("foo:child")), STR("namespace-uri()"), STR("http://foo2"));
 -	
 -	// namespace-uri with 1 non-node-set argument
 -	CHECK_XPATH_FAIL(STR("namespace-uri(1)"));
 -
 -	// namespace-uri with 1 node-set argument
 -	CHECK_XPATH_STRING(n, STR("namespace-uri(c1)"), STR(""));
 -	CHECK_XPATH_STRING(n, STR("namespace-uri(c5/child::node())"), STR("http://foo"));
 -	CHECK_XPATH_STRING(n, STR("namespace-uri(c2/attribute::node())"), STR("http://foo2"));
 -	CHECK_XPATH_STRING(n, STR("namespace-uri(c2/child::node())"), STR("http://foo2"));
 -	CHECK_XPATH_STRING(n, STR("namespace-uri(c1/child::node())"), STR(""));
 -	CHECK_XPATH_STRING(n, STR("namespace-uri(c4/child::node())"), STR(""));
 -	CHECK_XPATH_STRING(n, STR("namespace-uri(c3)"), STR("http://def"));
 -	CHECK_XPATH_STRING(n, STR("namespace-uri(c3/@attr)"), STR("")); // the namespace name for an unprefixed attribute name always has no value (Namespaces in XML 1.0)
 -	CHECK_XPATH_STRING(n, STR("namespace-uri(c3/child::node())"), STR("http://def"));
 -	CHECK_XPATH_STRING(n, STR("namespace-uri(c6/@bar:attr)"), STR(""));
 -
 -	// namespace-uri with 2 arguments
 -	CHECK_XPATH_FAIL(STR("namespace-uri(c1, c2)"));
 -}
 -
 -TEST_XML_FLAGS(xpath_nodeset_name, "<node xmlns:foo='http://foo'><c1>text</c1><c2 xmlns:foo='http://foo2' foo:attr='value'><foo:child/></c2><c3 xmlns='http://def' attr='value'><child/></c3><c4><?target stuff?></c4></node>", parse_default | parse_pi)
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	// name with 0 arguments
 -	CHECK_XPATH_STRING(c, STR("name()"), STR(""));
 -	CHECK_XPATH_STRING(n, STR("name()"), STR("node"));
 -	
 -	// name with 1 non-node-set argument
 -	CHECK_XPATH_FAIL(STR("name(1)"));
 -
 -	// name with 1 node-set argument
 -	CHECK_XPATH_STRING(n, STR("name(c1)"), STR("c1"));
 -	CHECK_XPATH_STRING(n, STR("name(c2/node())"), STR("foo:child"));
 -	CHECK_XPATH_STRING(n, STR("name(c2/attribute::node())"), STR("foo:attr"));
 -	CHECK_XPATH_STRING(n, STR("name(c1/node())"), STR(""));
 -	CHECK_XPATH_STRING(n, STR("name(c4/node())"), STR("target"));
 -	CHECK_XPATH_STRING(n, STR("name(c1/following-sibling::node())"), STR("c2"));
 -	CHECK_XPATH_STRING(n, STR("name(c4/preceding-sibling::node())"), STR("c1"));
 -
 -	// name with 2 arguments
 -	CHECK_XPATH_FAIL(STR("name(c1, c2)"));
 -}
 -
 -TEST(xpath_function_arguments)
 -{
 -	xml_node c;
 -
 -	// conversion to string
 -	CHECK_XPATH_NUMBER(c, STR("string-length(12)"), 2);
 -	
 -	// conversion to number
 -	CHECK_XPATH_NUMBER(c, STR("round('1.2')"), 1);
 -	CHECK_XPATH_NUMBER(c, STR("round('1.7')"), 2);
 -
 -	// conversion to boolean
 -	CHECK_XPATH_BOOLEAN(c, STR("not('1')"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("not('')"), true);
 -	
 -	// conversion to node set
 -	CHECK_XPATH_FAIL(STR("sum(1)"));
 -
 -	// expression evaluation
 -	CHECK_XPATH_NUMBER(c, STR("round((2 + 2 * 2) div 4)"), 2);
 -	
 -	// empty expressions
 -	CHECK_XPATH_FAIL(STR("round(,)"));
 -	CHECK_XPATH_FAIL(STR("substring(,)"));
 -	CHECK_XPATH_FAIL(STR("substring('a',)"));
 -	CHECK_XPATH_FAIL(STR("substring(,'a')"));
 -
 -	// extra commas
 -	CHECK_XPATH_FAIL(STR("round(,1)"));
 -	CHECK_XPATH_FAIL(STR("round(1,)"));
 -
 -	// lack of commas
 -	CHECK_XPATH_FAIL(STR("substring(1 2)"));
 -
 -	// whitespace after function name
 -	CHECK_XPATH_BOOLEAN(c, STR("true ()"), true);
 -
 -	// too many arguments
 -	CHECK_XPATH_FAIL(STR("round(1, 2, 3, 4, 5, 6)"));
 -}
 -
 -TEST_XML_FLAGS(xpath_string_value, "<node><c1>pcdata</c1><c2><child/></c2><c3 attr='avalue'/><c4><?target pivalue?></c4><c5><!--comment--></c5><c6><![CDATA[cdata]]></c6></node>", parse_default | parse_pi | parse_comments)
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_STRING(c, STR("string()"), STR(""));
 -	CHECK_XPATH_STRING(doc, STR("string()"), STR("pcdatacdata"));
 -	CHECK_XPATH_STRING(n, STR("string()"), STR("pcdatacdata"));
 -	CHECK_XPATH_STRING(n, STR("string(c1/node())"), STR("pcdata"));
 -	CHECK_XPATH_STRING(n, STR("string(c2/node())"), STR(""));
 -	CHECK_XPATH_STRING(n, STR("string(c3/@attr)"), STR("avalue"));
 -	CHECK_XPATH_STRING(n, STR("string(c4/node())"), STR("pivalue"));
 -	CHECK_XPATH_STRING(n, STR("string(c5/node())"), STR("comment"));
 -	CHECK_XPATH_STRING(n, STR("string(c6/node())"), STR("cdata"));
 -}
 -
 -TEST_XML(xpath_string_concat_translate, "<node>foobar</node>")
 -{
 -	CHECK_XPATH_STRING(doc, STR("concat('a', 'b', 'c', translate(node, 'o', 'a'), 'd')"), STR("abcfaabard"));
 -}
 -
 -#endif
 +#ifndef PUGIXML_NO_XPATH + +#include "common.hpp" + +TEST_XML(xpath_number_number, "<node>123</node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")).first_child(); +	 +	// number with 0 arguments +	CHECK_XPATH_NUMBER_NAN(c, STR("number()")); +	CHECK_XPATH_NUMBER(n, STR("number()"), 123); + +	// number with 1 string argument +	CHECK_XPATH_NUMBER(c, STR("number(' -123.456 ')"), -123.456); +	CHECK_XPATH_NUMBER(c, STR("number(' -123.')"), -123); +	CHECK_XPATH_NUMBER(c, STR("number('123.')"), 123); +	CHECK_XPATH_NUMBER(c, STR("number('.56')"), 0.56); +	CHECK_XPATH_NUMBER(c, STR("number('123 ')"), 123); +	CHECK_XPATH_NUMBER_NAN(c, STR("number('foobar')")); +	CHECK_XPATH_NUMBER_NAN(c, STR("number('f1')")); +	CHECK_XPATH_NUMBER_NAN(c, STR("number('1f')")); +	CHECK_XPATH_NUMBER_NAN(c, STR("number('1.f')")); +	CHECK_XPATH_NUMBER_NAN(c, STR("number('1.0f')")); +	CHECK_XPATH_NUMBER_NAN(c, STR("number('123 f')")); +	CHECK_XPATH_NUMBER_NAN(c, STR("number('')")); +	CHECK_XPATH_NUMBER_NAN(c, STR("number('.')")); + +	// number with 1 bool argument +	CHECK_XPATH_NUMBER(c, STR("number(true())"), 1); +	CHECK_XPATH_NUMBER(c, STR("number(false())"), 0); + +	// number with 1 node set argument +	CHECK_XPATH_NUMBER(n, STR("number(.)"), 123); + +	// number with 1 number argument +	CHECK_XPATH_NUMBER(c, STR("number(1)"), 1); +	 +	// number with 2 arguments +	CHECK_XPATH_FAIL(STR("number(1, 2)")); +} + +TEST_XML(xpath_number_sum, "<node>123<child>789</child></node><node/>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); +	 +	// sum with 0 arguments +	CHECK_XPATH_FAIL(STR("sum()")); + +	// sum with 1 argument +	CHECK_XPATH_NUMBER(c, STR("sum(.)"), 0); +	CHECK_XPATH_NUMBER(n, STR("sum(.)"), 123789); // 123 .. 789 +	 +	CHECK_XPATH_NUMBER(n, STR("sum(./descendant-or-self::node())"), 125490); // node + 123 + child + 789 = 123789 + 123 + 789 + 789 = 125490 +	CHECK_XPATH_NUMBER(n, STR("sum(.//node())"), 1701); // 123 + child + 789 = 123 + 789 + 789 +	CHECK_XPATH_NUMBER_NAN(doc.last_child(), STR("sum(.)")); + +	// sum with 2 arguments +	CHECK_XPATH_FAIL(STR("sum(1, 2)")); +	 +	// sum with 1 non-node-set argument +	CHECK_XPATH_FAIL(STR("sum(1)")); +} + +TEST(xpath_number_floor) +{ +	xml_node c; + +	// floor with 0 arguments +	CHECK_XPATH_FAIL(STR("floor()")); + +	// floor with 1 argument +	CHECK_XPATH_NUMBER(c, STR("floor(0)"), 0); +	CHECK_XPATH_NUMBER(c, STR("floor(1.2)"), 1); +	CHECK_XPATH_NUMBER(c, STR("floor(1)"), 1); +	CHECK_XPATH_NUMBER(c, STR("floor(-1.2)"), -2); +	CHECK_XPATH_NUMBER_NAN(c, STR("floor(string('nan'))")); +	CHECK_XPATH_STRING(c, STR("string(floor(1 div 0))"), STR("Infinity")); +	CHECK_XPATH_STRING(c, STR("string(floor(-1 div 0))"), STR("-Infinity")); + +	// floor with 2 arguments +	CHECK_XPATH_FAIL(STR("floor(1, 2)")); + +	// floor with argument 0 should return 0 +	CHECK_XPATH_STRING(c, STR("string(1 div floor(0))"), STR("Infinity")); + +	// floor with argument -0 should return -0 +#if !(defined(__APPLE__) && defined(__MACH__)) // MacOS X gcc 4.0.1 implements floor incorrectly (floor never returns -0) +	CHECK_XPATH_STRING(c, STR("string(1 div floor(-0))"), STR("-Infinity")); +#endif +} + +TEST(xpath_number_ceiling) +{ +	xml_node c; + +	// ceiling with 0 arguments +	CHECK_XPATH_FAIL(STR("ceiling()")); + +	// ceiling with 1 argument +	CHECK_XPATH_NUMBER(c, STR("ceiling(0)"), 0); +	CHECK_XPATH_NUMBER(c, STR("ceiling(1.2)"), 2); +	CHECK_XPATH_NUMBER(c, STR("ceiling(1)"), 1); +	CHECK_XPATH_NUMBER(c, STR("ceiling(-1.2)"), -1); +	CHECK_XPATH_NUMBER_NAN(c, STR("ceiling(string('nan'))")); +	CHECK_XPATH_STRING(c, STR("string(ceiling(1 div 0))"), STR("Infinity")); +	CHECK_XPATH_STRING(c, STR("string(ceiling(-1 div 0))"), STR("-Infinity")); + +	// ceiling with 2 arguments +	CHECK_XPATH_FAIL(STR("ceiling(1, 2)")); + +	// ceiling with argument 0 should return 0 +	CHECK_XPATH_STRING(c, STR("string(1 div ceiling(0))"), STR("Infinity")); + +	// ceiling with argument in range (-1, -0] should result in minus zero +#if !(defined(__APPLE__) && defined(__MACH__)) // MacOS X gcc 4.0.1 implements ceil incorrectly (ceil never returns -0) +	CHECK_XPATH_STRING(c, STR("string(1 div ceiling(-0))"), STR("-Infinity")); +	CHECK_XPATH_STRING(c, STR("string(1 div ceiling(-0.1))"), STR("-Infinity")); +#endif +} + +TEST(xpath_number_round) +{ +	xml_node c; + +	// round with 0 arguments +	CHECK_XPATH_FAIL(STR("round()")); + +	// round with 1 argument +	CHECK_XPATH_NUMBER(c, STR("round(1.2)"), 1); +	CHECK_XPATH_NUMBER(c, STR("round(1.5)"), 2); +	CHECK_XPATH_NUMBER(c, STR("round(1.8)"), 2); +	CHECK_XPATH_NUMBER(c, STR("round(1)"), 1); +	CHECK_XPATH_NUMBER(c, STR("round(-1.2)"), -1); +	CHECK_XPATH_NUMBER(c, STR("round(-1.5)"), -1); +	CHECK_XPATH_NUMBER(c, STR("round(-1.6)"), -2); +	CHECK_XPATH_NUMBER_NAN(c, STR("round(string('nan'))")); +	CHECK_XPATH_STRING(c, STR("string(round(1 div 0))"), STR("Infinity")); +	CHECK_XPATH_STRING(c, STR("string(round(-1 div 0))"), STR("-Infinity")); + +	// round with 2 arguments +	CHECK_XPATH_FAIL(STR("round(1, 2)")); + +	// round with argument in range [-0.5, -0] should result in minus zero +	CHECK_XPATH_STRING(c, STR("string(1 div round(0))"), STR("Infinity")); + +#if !(defined(__APPLE__) && defined(__MACH__)) // MacOS X gcc 4.0.1 implements ceil incorrectly (ceil never returns -0) +	CHECK_XPATH_STRING(c, STR("string(1 div round(-0.5))"), STR("-Infinity")); +	CHECK_XPATH_STRING(c, STR("string(1 div round(-0))"), STR("-Infinity")); +	CHECK_XPATH_STRING(c, STR("string(1 div round(-0.1))"), STR("-Infinity")); +#endif +} + +TEST_XML(xpath_boolean_boolean, "<node />") +{ +	xml_node c; +	 +	// boolean with 0 arguments +	CHECK_XPATH_FAIL(STR("boolean()")); + +	// boolean with 1 number argument +	CHECK_XPATH_BOOLEAN(c, STR("boolean(0)"), false); +	CHECK_XPATH_BOOLEAN(c, STR("boolean(1)"), true); +	CHECK_XPATH_BOOLEAN(c, STR("boolean(-1)"), true); +	CHECK_XPATH_BOOLEAN(c, STR("boolean(0.1)"), true); +	CHECK_XPATH_BOOLEAN(c, STR("boolean(number('nan'))"), false); + +	// boolean with 1 string argument +	CHECK_XPATH_BOOLEAN(c, STR("boolean('x')"), true); +	CHECK_XPATH_BOOLEAN(c, STR("boolean('')"), false); + +	// boolean with 1 node set argument +	CHECK_XPATH_BOOLEAN(c, STR("boolean(.)"), false); +	CHECK_XPATH_BOOLEAN(doc, STR("boolean(.)"), true); +	CHECK_XPATH_BOOLEAN(doc, STR("boolean(foo)"), false); + +	// boolean with 2 arguments +	CHECK_XPATH_FAIL(STR("boolean(1, 2)")); +} + +TEST(xpath_boolean_not) +{ +	xml_node c; +	 +	// not with 0 arguments +	CHECK_XPATH_FAIL(STR("not()")); + +	// not with 1 argument +	CHECK_XPATH_BOOLEAN(c, STR("not(true())"), false); +	CHECK_XPATH_BOOLEAN(c, STR("not(false())"), true); +	 +	// boolean with 2 arguments +	CHECK_XPATH_FAIL(STR("not(1, 2)")); +} + +TEST(xpath_boolean_true) +{ +	xml_node c; +	 +	// true with 0 arguments +	CHECK_XPATH_BOOLEAN(c, STR("true()"), true); + +	// true with 1 argument +	CHECK_XPATH_FAIL(STR("true(1)")); +} + +TEST(xpath_boolean_false) +{ +	xml_node c; +	 +	// false with 0 arguments +	CHECK_XPATH_BOOLEAN(c, STR("false()"), false); + +	// false with 1 argument +	CHECK_XPATH_FAIL(STR("false(1)")); +} + +TEST_XML(xpath_boolean_lang, "<node xml:lang='en'><child xml:lang='ru-UK'><subchild/></child></node><foo><bar/></foo>") +{ +	xml_node c; +	 +	// lang with 0 arguments +	CHECK_XPATH_FAIL(STR("lang()")); + +	// lang with 1 argument, no language +	CHECK_XPATH_BOOLEAN(c, STR("lang('en')"), false); +	CHECK_XPATH_BOOLEAN(doc.child(STR("foo")), STR("lang('en')"), false); +	CHECK_XPATH_BOOLEAN(doc.child(STR("foo")), STR("lang('')"), false); +	CHECK_XPATH_BOOLEAN(doc.child(STR("foo")).child(STR("bar")), STR("lang('en')"), false); +	 +	// lang with 1 argument, same language/prefix +	CHECK_XPATH_BOOLEAN(doc.child(STR("node")), STR("lang('en')"), true); +	CHECK_XPATH_BOOLEAN(doc.child(STR("node")).child(STR("child")), STR("lang('ru-uk')"), true); +	CHECK_XPATH_BOOLEAN(doc.child(STR("node")).child(STR("child")), STR("lang('ru')"), true); +	CHECK_XPATH_BOOLEAN(doc.child(STR("node")).child(STR("child")).child(STR("subchild")), STR("lang('ru')"), true); +	CHECK_XPATH_BOOLEAN(doc.child(STR("node")).child(STR("child")).child(STR("subchild")), STR("lang('RU')"), true); + +	// lang with 1 argument, different language/prefix +	CHECK_XPATH_BOOLEAN(doc.child(STR("node")), STR("lang('')"), false); +	CHECK_XPATH_BOOLEAN(doc.child(STR("node")), STR("lang('e')"), false); +	CHECK_XPATH_BOOLEAN(doc.child(STR("node")).child(STR("child")), STR("lang('en')"), false); +	CHECK_XPATH_BOOLEAN(doc.child(STR("node")).child(STR("child")), STR("lang('ru-gb')"), false); +	CHECK_XPATH_BOOLEAN(doc.child(STR("node")).child(STR("child")), STR("lang('r')"), false); +	CHECK_XPATH_BOOLEAN(doc.child(STR("node")).child(STR("child")).child(STR("subchild")), STR("lang('en')"), false); + +	// lang with 2 arguments +	CHECK_XPATH_FAIL(STR("lang(1, 2)")); +} + +TEST_XML(xpath_string_string, "<node>123<child id='1'>789</child><child><![CDATA[200]]></child>100</node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	// string with 0 arguments +	CHECK_XPATH_STRING(c, STR("string()"), STR("")); +	CHECK_XPATH_STRING(n.child(STR("child")), STR("string()"), STR("789")); + +	// string with 1 node-set argument +	CHECK_XPATH_STRING(n, STR("string(child)"), STR("789")); +	CHECK_XPATH_STRING(n, STR("string(child/@id)"), STR("1")); +	CHECK_XPATH_STRING(n, STR("string(.)"), STR("123789200100")); + +	// string with 1 number argument +	CHECK_XPATH_STRING(c, STR("string(0 div 0)"), STR("NaN")); +	CHECK_XPATH_STRING(c, STR("string(0)"), STR("0")); +	CHECK_XPATH_STRING(c, STR("string(-0)"), STR("0")); +	CHECK_XPATH_STRING(c, STR("string(1 div 0)"), STR("Infinity")); +	CHECK_XPATH_STRING(c, STR("string(-1 div -0)"), STR("Infinity")); +	CHECK_XPATH_STRING(c, STR("string(-1 div 0)"), STR("-Infinity")); +	CHECK_XPATH_STRING(c, STR("string(1 div -0)"), STR("-Infinity")); +	CHECK_XPATH_STRING(c, STR("string(1234567)"), STR("1234567")); +	CHECK_XPATH_STRING(c, STR("string(-1234567)"), STR("-1234567")); +	CHECK_XPATH_STRING(c, STR("string(1234.5678)"), STR("1234.5678")); +	CHECK_XPATH_STRING(c, STR("string(-1234.5678)"), STR("-1234.5678")); +	CHECK_XPATH_STRING(c, STR("string(0.5678)"), STR("0.5678")); +	CHECK_XPATH_STRING(c, STR("string(-0.5678)"), STR("-0.5678")); +	CHECK_XPATH_STRING(c, STR("string(0.0)"), STR("0")); +	CHECK_XPATH_STRING(c, STR("string(-0.0)"), STR("0")); + +	// string with 1 boolean argument +	CHECK_XPATH_STRING(c, STR("string(true())"), STR("true")); +	CHECK_XPATH_STRING(c, STR("string(false())"), STR("false")); + +	// string with 1 string argument +	CHECK_XPATH_STRING(c, STR("string('abc')"), STR("abc")); + +	// string with 2 arguments +	CHECK_XPATH_FAIL(STR("string(1, 2)")); +} + +TEST(xpath_string_concat) +{ +	xml_node c; + +	// concat with 0 arguments +	CHECK_XPATH_FAIL(STR("concat()")); + +	// concat with 1 argument +	CHECK_XPATH_FAIL(STR("concat('')")); + +	// concat with exactly 2 arguments +	CHECK_XPATH_STRING(c, STR("concat('prev','next')"), STR("prevnext")); +	CHECK_XPATH_STRING(c, STR("concat('','next')"), STR("next")); +	CHECK_XPATH_STRING(c, STR("concat('prev','')"), STR("prev")); + +	// concat with 3 or more arguments +	CHECK_XPATH_STRING(c, STR("concat('a', 'b', 'c')"), STR("abc")); +	CHECK_XPATH_STRING(c, STR("concat('a', 'b', 'c', 'd')"), STR("abcd")); +	CHECK_XPATH_STRING(c, STR("concat('a', 'b', 'c', 'd', 'e')"), STR("abcde")); +	CHECK_XPATH_STRING(c, STR("concat('a', 'b', 'c', 'd', 'e', 'f')"), STR("abcdef")); +	CHECK_XPATH_STRING(c, STR("concat('a', 'b', 'c', 'd', 'e', 'f', 'g')"), STR("abcdefg")); +	CHECK_XPATH_STRING(c, STR("concat(1, 2, 3, 4, 5, 6, 7, 8)"), STR("12345678")); +} + +TEST(xpath_string_starts_with) +{ +	xml_node c; + +	// starts-with with 0 arguments +	CHECK_XPATH_FAIL(STR("starts-with()")); + +	// starts-with with 1 argument +	CHECK_XPATH_FAIL(STR("starts-with('a')")); + +	// starts-with with 2 arguments +	CHECK_XPATH_BOOLEAN(c, STR("starts-with('abc', '')"), true); +	CHECK_XPATH_BOOLEAN(c, STR("starts-with('abc', 'a')"), true); +	CHECK_XPATH_BOOLEAN(c, STR("starts-with('abc', 'abc')"), true); +	CHECK_XPATH_BOOLEAN(c, STR("starts-with('abc', 'abcd')"), false); +	CHECK_XPATH_BOOLEAN(c, STR("starts-with('bc', 'c')"), false); +	CHECK_XPATH_BOOLEAN(c, STR("starts-with('', 'c')"), false); +	CHECK_XPATH_BOOLEAN(c, STR("starts-with('', '')"), true); + +	// starts-with with 3 arguments +	CHECK_XPATH_FAIL(STR("starts-with('a', 'b', 'c')")); +} + +TEST(xpath_string_contains) +{ +	xml_node c; + +	// contains with 0 arguments +	CHECK_XPATH_FAIL(STR("contains()")); + +	// contains with 1 argument +	CHECK_XPATH_FAIL(STR("contains('a')")); + +	// contains with 2 arguments +	CHECK_XPATH_BOOLEAN(c, STR("contains('abc', '')"), true); +	CHECK_XPATH_BOOLEAN(c, STR("contains('abc', 'a')"), true); +	CHECK_XPATH_BOOLEAN(c, STR("contains('abc', 'abc')"), true); +	CHECK_XPATH_BOOLEAN(c, STR("contains('abcd', 'bc')"), true); +	CHECK_XPATH_BOOLEAN(c, STR("contains('abc', 'abcd')"), false); +	CHECK_XPATH_BOOLEAN(c, STR("contains('b', 'bc')"), false); +	CHECK_XPATH_BOOLEAN(c, STR("contains('', 'c')"), false); +	CHECK_XPATH_BOOLEAN(c, STR("contains('', '')"), true); + +	// contains with 3 arguments +	CHECK_XPATH_FAIL(STR("contains('a', 'b', 'c')")); +} + +TEST(xpath_string_substring_before) +{ +	xml_node c; + +	// substring-before with 0 arguments +	CHECK_XPATH_FAIL(STR("substring-before()")); + +	// substring-before with 1 argument +	CHECK_XPATH_FAIL(STR("substring-before('a')")); +	 +	// substring-before with 2 arguments +	CHECK_XPATH_STRING(c, STR("substring-before('abc', 'abc')"), STR("")); +	CHECK_XPATH_STRING(c, STR("substring-before('abc', 'a')"), STR("")); +	CHECK_XPATH_STRING(c, STR("substring-before('abc', 'cd')"), STR("")); +	CHECK_XPATH_STRING(c, STR("substring-before('abc', 'b')"), STR("a")); +	CHECK_XPATH_STRING(c, STR("substring-before('abc', 'c')"), STR("ab")); +	CHECK_XPATH_STRING(c, STR("substring-before('', '')"), STR("")); +	 +	// substring-before with 2 arguments, from W3C standard +	CHECK_XPATH_STRING(c, STR("substring-before(\"1999/04/01\",\"/\")"), STR("1999")); + +	// substring-before with 3 arguments +	CHECK_XPATH_FAIL(STR("substring-before('a', 'b', 'c')")); +} + +TEST(xpath_string_substring_after) +{ +	xml_node c; + +	// substring-after with 0 arguments +	CHECK_XPATH_FAIL(STR("substring-after()")); + +	// substring-after with 1 argument +	CHECK_XPATH_FAIL(STR("substring-after('a')")); +	 +	// substring-after with 2 arguments +	CHECK_XPATH_STRING(c, STR("substring-after('abc', 'abc')"), STR("")); +	CHECK_XPATH_STRING(c, STR("substring-after('abc', 'a')"), STR("bc")); +	CHECK_XPATH_STRING(c, STR("substring-after('abc', 'cd')"), STR("")); +	CHECK_XPATH_STRING(c, STR("substring-after('abc', 'b')"), STR("c")); +	CHECK_XPATH_STRING(c, STR("substring-after('abc', 'c')"), STR("")); +	CHECK_XPATH_STRING(c, STR("substring-after('', '')"), STR("")); + +	// substring-before with 2 arguments, from W3C standard +	CHECK_XPATH_STRING(c, STR("substring-after(\"1999/04/01\",\"/\")"), STR("04/01")); +	CHECK_XPATH_STRING(c, STR("substring-after(\"1999/04/01\",\"19\")"), STR("99/04/01")); + +	// substring-after with 3 arguments +	CHECK_XPATH_FAIL(STR("substring-after('a', 'b', 'c')")); +} + +TEST(xpath_string_substring) +{ +	xml_node c; + +	// substring with 0 arguments +	CHECK_XPATH_FAIL(STR("substring()")); +	 +	// substring with 1 argument +	CHECK_XPATH_FAIL(STR("substring('')")); +	 +	// substring with 2 arguments +	CHECK_XPATH_STRING(c, STR("substring('abcd', 2)"), STR("bcd")); +	CHECK_XPATH_STRING(c, STR("substring('abcd', 1)"), STR("abcd")); +	CHECK_XPATH_STRING(c, STR("substring('abcd', 1.1)"), STR("abcd")); +	CHECK_XPATH_STRING(c, STR("substring('abcd', 1.5)"), STR("bcd")); +	CHECK_XPATH_STRING(c, STR("substring('abcd', 1.8)"), STR("bcd")); +	CHECK_XPATH_STRING(c, STR("substring('abcd', 10)"), STR("")); +	CHECK_XPATH_STRING(c, STR("substring('abcd', 0)"), STR("abcd")); +	CHECK_XPATH_STRING(c, STR("substring('abcd', -100)"), STR("abcd")); +	CHECK_XPATH_STRING(c, STR("substring('abcd', -1 div 0)"), STR("abcd")); +	CHECK_XPATH_STRING(c, STR("substring('abcd', 1 div 0)"), STR("")); +	CHECK_XPATH_STRING(c, STR("substring('abcd', 0 div 0)"), STR("")); +	CHECK_XPATH_STRING(c, STR("substring('', 1)"), STR("")); +	CHECK_XPATH_STRING(c, STR("substring('', 0)"), STR("")); +    CHECK_XPATH_STRING(c, STR("substring(substring('internalexternalcorrect substring',9),9)"), STR("correct substring")); + +	// substring with 3 arguments +	CHECK_XPATH_STRING(c, STR("substring('abcd', 2, 1)"), STR("b")); +	CHECK_XPATH_STRING(c, STR("substring('abcd', 2, 2)"), STR("bc")); +	CHECK_XPATH_STRING(c, STR("substring('abcd', 1, 0)"), STR("")); +	CHECK_XPATH_STRING(c, STR("substring('abcd', 1, 0.4)"), STR("")); +	CHECK_XPATH_STRING(c, STR("substring('abcd', 1, 0.5)"), STR("a")); +	CHECK_XPATH_STRING(c, STR("substring('abcd', 10, -5)"), STR("")); +	CHECK_XPATH_STRING(c, STR("substring('abcd', 0, -1)"), STR("")); +	CHECK_XPATH_STRING(c, STR("substring('abcd', -100, 100)"), STR("abcd")); +	CHECK_XPATH_STRING(c, STR("substring('abcd', -1 div 0, 4)"), STR("")); +	CHECK_XPATH_STRING(c, STR("substring('abcd', 1 div 0, 0 div 0)"), STR("")); +	CHECK_XPATH_STRING(c, STR("substring('abcd', 0 div 0, 1)"), STR("")); +	CHECK_XPATH_STRING(c, STR("substring('', 1, 2)"), STR("")); +	CHECK_XPATH_STRING(c, STR("substring('', 0, 0)"), STR("")); + +	// substring with 3 arguments, from W3C standard +	CHECK_XPATH_STRING(c, STR("substring('12345', 1.5, 2.6)"), STR("234")); +	CHECK_XPATH_STRING(c, STR("substring('12345', 0, 3)"), STR("12")); +	CHECK_XPATH_STRING(c, STR("substring('12345', 0 div 0, 3)"), STR("")); +	CHECK_XPATH_STRING(c, STR("substring('12345', 1, 0 div 0)"), STR("")); +	CHECK_XPATH_STRING(c, STR("substring('12345', -42, 1 div 0)"), STR("12345")); +	CHECK_XPATH_STRING(c, STR("substring('12345', -1 div 0, 1 div 0)"), STR("")); + +	// substring with 4 arguments +	CHECK_XPATH_FAIL(STR("substring('', 1, 2, 3)")); +} + +TEST_XML(xpath_string_string_length, "<node>123</node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	// string-length with 0 arguments +	CHECK_XPATH_NUMBER(c, STR("string-length()"), 0); +	CHECK_XPATH_NUMBER(n, STR("string-length()"), 3); + +	// string-length with 1 argument +	CHECK_XPATH_NUMBER(c, STR("string-length('')"), 0); +	CHECK_XPATH_NUMBER(c, STR("string-length('a')"), 1); +	CHECK_XPATH_NUMBER(c, STR("string-length('abcdef')"), 6); + +	// string-length with 2 arguments +	CHECK_XPATH_FAIL(STR("string-length(1, 2)")); +} + +TEST_XML_FLAGS(xpath_string_normalize_space, "<node> \t\r\rval1  \rval2\r\nval3\nval4\r\r</node>", parse_minimal) +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	// normalize-space with 0 arguments +	CHECK_XPATH_STRING(c, STR("normalize-space()"), STR("")); +	CHECK_XPATH_STRING(n, STR("normalize-space()"), STR("val1 val2 val3 val4")); +	 +	// normalize-space with 1 argument +	CHECK_XPATH_STRING(c, STR("normalize-space('')"), STR("")); +	CHECK_XPATH_STRING(c, STR("normalize-space('abcd')"), STR("abcd")); +	CHECK_XPATH_STRING(c, STR("normalize-space(' \r\nabcd')"), STR("abcd")); +	CHECK_XPATH_STRING(c, STR("normalize-space('abcd \n\r')"), STR("abcd")); +	CHECK_XPATH_STRING(c, STR("normalize-space('ab\r\n\tcd')"), STR("ab cd")); +	CHECK_XPATH_STRING(c, STR("normalize-space('ab    cd')"), STR("ab cd")); +	CHECK_XPATH_STRING(c, STR("normalize-space('\07')"), STR("\07")); +	 +	// normalize-space with 2 arguments +	CHECK_XPATH_FAIL(STR("normalize-space(1, 2)")); +} + +TEST(xpath_string_translate) +{ +	xml_node c; + +	// translate with 0 arguments +	CHECK_XPATH_FAIL(STR("translate()")); +	 +	// translate with 1 argument +	CHECK_XPATH_FAIL(STR("translate('a')")); + +	// translate with 2 arguments +	CHECK_XPATH_FAIL(STR("translate('a', 'b')")); +	 +	// translate with 3 arguments +	CHECK_XPATH_STRING(c, STR("translate('abc', '', '')"), STR("abc")); +	CHECK_XPATH_STRING(c, STR("translate('abc', '', 'foo')"), STR("abc")); +	CHECK_XPATH_STRING(c, STR("translate('abc', 'ab', 'ba')"), STR("bac")); +	CHECK_XPATH_STRING(c, STR("translate('abc', 'ab', 'f')"), STR("fc")); +	CHECK_XPATH_STRING(c, STR("translate('abc', 'aabb', '1234')"), STR("13c")); +	CHECK_XPATH_STRING(c, STR("translate('', 'abc', 'bac')"), STR("")); + +	// translate with 3 arguments, from W3C standard +	CHECK_XPATH_STRING(c, STR("translate('bar','abc','ABC')"), STR("BAr")); +	CHECK_XPATH_STRING(c, STR("translate('--aaa--','abc-','ABC')"), STR("AAA")); + +	// translate with 4 arguments +	CHECK_XPATH_FAIL(STR("translate('a', 'b', 'c', 'd')")); +} + +TEST_XML(xpath_nodeset_last, "<node><c1/><c1/><c2/><c3/><c3/><c3/><c3/></node>") +{ +	xml_node n = doc.child(STR("node")); + +	// last with 0 arguments +	CHECK_XPATH_NUMBER(n, STR("last()"), 1); +	CHECK_XPATH_NODESET(n, STR("c1[last() = 1]")); +	CHECK_XPATH_NODESET(n, STR("c1[last() = 2]")) % 3 % 4; // c1, c1 +	CHECK_XPATH_NODESET(n, STR("c2/preceding-sibling::node()[last() = 2]")) % 4 % 3; // c1, c1 + +	// last with 1 argument +	CHECK_XPATH_FAIL(STR("last(c)")); +} + +TEST_XML(xpath_nodeset_position, "<node><c1/><c1/><c2/><c3/><c3/><c3/><c3/></node>") +{ +	xml_node n = doc.child(STR("node")); + +	// position with 0 arguments +	CHECK_XPATH_NUMBER(n, STR("position()"), 1); +	CHECK_XPATH_NODESET(n, STR("c1[position() = 0]")); +	CHECK_XPATH_NODESET(n, STR("c1[position() = 1]")) % 3; +	CHECK_XPATH_NODESET(n, STR("c1[position() = 2]")) % 4; +	CHECK_XPATH_NODESET(n, STR("c1[position() = 3]")); +	CHECK_XPATH_NODESET(n, STR("c2/preceding-sibling::node()[position() = 1]")) % 4; +	CHECK_XPATH_NODESET(n, STR("c2/preceding-sibling::node()[position() = 2]")) % 3; +	 +	// position with 1 argument +	CHECK_XPATH_FAIL(STR("position(c)")); +} + +TEST_XML(xpath_nodeset_count, "<node><c1/><c1/><c2/><c3/><c3/><c3/><c3/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	// count with 0 arguments +	CHECK_XPATH_FAIL(STR("count()")); + +	// count with 1 non-node-set argument +	CHECK_XPATH_FAIL(STR("count(1)")); +	CHECK_XPATH_FAIL(STR("count(true())")); +	CHECK_XPATH_FAIL(STR("count('')")); + +	// count with 1 node-set argument +	CHECK_XPATH_NUMBER(c, STR("count(.)"), 0); +	CHECK_XPATH_NUMBER(n, STR("count(.)"), 1); +	CHECK_XPATH_NUMBER(n, STR("count(c1)"), 2); +	CHECK_XPATH_NUMBER(n, STR("count(c2)"), 1); +	CHECK_XPATH_NUMBER(n, STR("count(c3)"), 4); +	CHECK_XPATH_NUMBER(n, STR("count(c4)"), 0); + +	// count with 2 arguments +	CHECK_XPATH_FAIL(STR("count(x, y)")); +} + +TEST_XML(xpath_nodeset_id, "<node id='foo'/>") +{ +	xml_node n = doc.child(STR("node")); + +	// id with 0 arguments +	CHECK_XPATH_FAIL(STR("id()")); +	 +	// id with 1 argument - no DTD => no id +	CHECK_XPATH_NODESET(n, STR("id('foo')")); + +	// id with 2 arguments +	CHECK_XPATH_FAIL(STR("id(1, 2)")); +} + +TEST_XML_FLAGS(xpath_nodeset_local_name, "<node xmlns:foo='http://foo'><c1>text</c1><c2 xmlns:foo='http://foo2' foo:attr='value'><foo:child/></c2><c3 xmlns='http://def' attr='value'><child/></c3><c4><?target stuff?></c4></node>", parse_default | parse_pi) +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	// local-name with 0 arguments +	CHECK_XPATH_STRING(c, STR("local-name()"), STR("")); +	CHECK_XPATH_STRING(n, STR("local-name()"), STR("node")); +	 +	// local-name with 1 non-node-set argument +	CHECK_XPATH_FAIL(STR("local-name(1)")); + +	// local-name with 1 node-set argument +	CHECK_XPATH_STRING(n, STR("local-name(c1)"), STR("c1")); +	CHECK_XPATH_STRING(n, STR("local-name(c2/node())"), STR("child")); +	CHECK_XPATH_STRING(n, STR("local-name(c2/attribute::node())"), STR("attr")); +	CHECK_XPATH_STRING(n, STR("local-name(c1/node())"), STR("")); +	CHECK_XPATH_STRING(n, STR("local-name(c4/node())"), STR("target")); +	CHECK_XPATH_STRING(n, STR("local-name(c1/following-sibling::node())"), STR("c2")); +	CHECK_XPATH_STRING(n, STR("local-name(c4/preceding-sibling::node())"), STR("c1")); + +	// local-name with 2 arguments +	CHECK_XPATH_FAIL(STR("local-name(c1, c2)")); +} + +TEST_XML_FLAGS(xpath_nodeset_namespace_uri, "<node xmlns:foo='http://foo'><c1>text</c1><c2 xmlns:foo='http://foo2' foo:attr='value'><foo:child/></c2><c3 xmlns='http://def' attr='value'><child/></c3><c4><?target stuff?></c4><c5><foo:child/></c5><c6 bar:attr=''/></node>", parse_default | parse_pi) +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	// namespace-uri with 0 arguments +	CHECK_XPATH_STRING(c, STR("namespace-uri()"), STR("")); +	CHECK_XPATH_STRING(n.child(STR("c2")).child(STR("foo:child")), STR("namespace-uri()"), STR("http://foo2")); +	 +	// namespace-uri with 1 non-node-set argument +	CHECK_XPATH_FAIL(STR("namespace-uri(1)")); + +	// namespace-uri with 1 node-set argument +	CHECK_XPATH_STRING(n, STR("namespace-uri(c1)"), STR("")); +	CHECK_XPATH_STRING(n, STR("namespace-uri(c5/child::node())"), STR("http://foo")); +	CHECK_XPATH_STRING(n, STR("namespace-uri(c2/attribute::node())"), STR("http://foo2")); +	CHECK_XPATH_STRING(n, STR("namespace-uri(c2/child::node())"), STR("http://foo2")); +	CHECK_XPATH_STRING(n, STR("namespace-uri(c1/child::node())"), STR("")); +	CHECK_XPATH_STRING(n, STR("namespace-uri(c4/child::node())"), STR("")); +	CHECK_XPATH_STRING(n, STR("namespace-uri(c3)"), STR("http://def")); +	CHECK_XPATH_STRING(n, STR("namespace-uri(c3/@attr)"), STR("")); // the namespace name for an unprefixed attribute name always has no value (Namespaces in XML 1.0) +	CHECK_XPATH_STRING(n, STR("namespace-uri(c3/child::node())"), STR("http://def")); +	CHECK_XPATH_STRING(n, STR("namespace-uri(c6/@bar:attr)"), STR("")); + +	// namespace-uri with 2 arguments +	CHECK_XPATH_FAIL(STR("namespace-uri(c1, c2)")); +} + +TEST_XML_FLAGS(xpath_nodeset_name, "<node xmlns:foo='http://foo'><c1>text</c1><c2 xmlns:foo='http://foo2' foo:attr='value'><foo:child/></c2><c3 xmlns='http://def' attr='value'><child/></c3><c4><?target stuff?></c4></node>", parse_default | parse_pi) +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	// name with 0 arguments +	CHECK_XPATH_STRING(c, STR("name()"), STR("")); +	CHECK_XPATH_STRING(n, STR("name()"), STR("node")); +	 +	// name with 1 non-node-set argument +	CHECK_XPATH_FAIL(STR("name(1)")); + +	// name with 1 node-set argument +	CHECK_XPATH_STRING(n, STR("name(c1)"), STR("c1")); +	CHECK_XPATH_STRING(n, STR("name(c2/node())"), STR("foo:child")); +	CHECK_XPATH_STRING(n, STR("name(c2/attribute::node())"), STR("foo:attr")); +	CHECK_XPATH_STRING(n, STR("name(c1/node())"), STR("")); +	CHECK_XPATH_STRING(n, STR("name(c4/node())"), STR("target")); +	CHECK_XPATH_STRING(n, STR("name(c1/following-sibling::node())"), STR("c2")); +	CHECK_XPATH_STRING(n, STR("name(c4/preceding-sibling::node())"), STR("c1")); + +	// name with 2 arguments +	CHECK_XPATH_FAIL(STR("name(c1, c2)")); +} + +TEST(xpath_function_arguments) +{ +	xml_node c; + +	// conversion to string +	CHECK_XPATH_NUMBER(c, STR("string-length(12)"), 2); +	 +	// conversion to number +	CHECK_XPATH_NUMBER(c, STR("round('1.2')"), 1); +	CHECK_XPATH_NUMBER(c, STR("round('1.7')"), 2); + +	// conversion to boolean +	CHECK_XPATH_BOOLEAN(c, STR("not('1')"), false); +	CHECK_XPATH_BOOLEAN(c, STR("not('')"), true); +	 +	// conversion to node set +	CHECK_XPATH_FAIL(STR("sum(1)")); + +	// expression evaluation +	CHECK_XPATH_NUMBER(c, STR("round((2 + 2 * 2) div 4)"), 2); +	 +	// empty expressions +	CHECK_XPATH_FAIL(STR("round(,)")); +	CHECK_XPATH_FAIL(STR("substring(,)")); +	CHECK_XPATH_FAIL(STR("substring('a',)")); +	CHECK_XPATH_FAIL(STR("substring(,'a')")); + +	// extra commas +	CHECK_XPATH_FAIL(STR("round(,1)")); +	CHECK_XPATH_FAIL(STR("round(1,)")); + +	// lack of commas +	CHECK_XPATH_FAIL(STR("substring(1 2)")); + +	// whitespace after function name +	CHECK_XPATH_BOOLEAN(c, STR("true ()"), true); + +	// too many arguments +	CHECK_XPATH_FAIL(STR("round(1, 2, 3, 4, 5, 6)")); +} + +TEST_XML_FLAGS(xpath_string_value, "<node><c1>pcdata</c1><c2><child/></c2><c3 attr='avalue'/><c4><?target pivalue?></c4><c5><!--comment--></c5><c6><![CDATA[cdata]]></c6></node>", parse_default | parse_pi | parse_comments) +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_STRING(c, STR("string()"), STR("")); +	CHECK_XPATH_STRING(doc, STR("string()"), STR("pcdatacdata")); +	CHECK_XPATH_STRING(n, STR("string()"), STR("pcdatacdata")); +	CHECK_XPATH_STRING(n, STR("string(c1/node())"), STR("pcdata")); +	CHECK_XPATH_STRING(n, STR("string(c2/node())"), STR("")); +	CHECK_XPATH_STRING(n, STR("string(c3/@attr)"), STR("avalue")); +	CHECK_XPATH_STRING(n, STR("string(c4/node())"), STR("pivalue")); +	CHECK_XPATH_STRING(n, STR("string(c5/node())"), STR("comment")); +	CHECK_XPATH_STRING(n, STR("string(c6/node())"), STR("cdata")); +} + +TEST_XML(xpath_string_concat_translate, "<node>foobar</node>") +{ +	CHECK_XPATH_STRING(doc, STR("concat('a', 'b', 'c', translate(node, 'o', 'a'), 'd')"), STR("abcfaabard")); +} + +#endif diff --git a/tests/test_xpath_operators.cpp b/tests/test_xpath_operators.cpp index b834b95..c7fe165 100644 --- a/tests/test_xpath_operators.cpp +++ b/tests/test_xpath_operators.cpp @@ -1,473 +1,473 @@ -#ifndef PUGIXML_NO_XPATH
 -
 -#include "common.hpp"
 -
 -TEST(xpath_operators_arithmetic)
 -{
 -	xml_node c;
 -
 -	// incorrect unary operator
 -	CHECK_XPATH_FAIL(STR("-"));
 -
 -	// correct unary operator
 -	CHECK_XPATH_NUMBER(c, STR("-1"), -1);
 -	CHECK_XPATH_NUMBER(c, STR("--1"), 1);
 -	CHECK_XPATH_NUMBER(c, STR("---1"), -1);
 -
 -	// incorrect binary operators
 -	CHECK_XPATH_FAIL(STR("5+"));
 -	CHECK_XPATH_FAIL(STR("5-"));
 -	CHECK_XPATH_FAIL(STR("5*"));
 -	CHECK_XPATH_FAIL(STR("+5"));
 -	CHECK_XPATH_FAIL(STR("*5"));
 -	CHECK_XPATH_FAIL(STR("1div2"));
 -	CHECK_XPATH_FAIL(STR("1mod"));
 -	CHECK_XPATH_FAIL(STR("1div"));
 -
 -	// correct trivial binary operators
 -	CHECK_XPATH_NUMBER(c, STR("1 + 2"), 3);
 -	CHECK_XPATH_NUMBER(c, STR("1+2"), 3);
 -	CHECK_XPATH_NUMBER(c, STR("1 * 2"), 2);
 -	CHECK_XPATH_NUMBER(c, STR("1*2"), 2);
 -	CHECK_XPATH_NUMBER(c, STR("1 div 2"), 0.5);
 -
 -	// operator precedence
 -	CHECK_XPATH_NUMBER(c, STR("2 + 2 * 2 div 1 mod 3"), 3);
 -	CHECK_XPATH_NUMBER(c, STR("2 + 2 * 2 div (1 mod 3)"), 6);
 -	CHECK_XPATH_NUMBER(c, STR("(2 + 2) * 2 div (1 mod 3)"), 8);
 -	CHECK_XPATH_NUMBER(c, STR("(2 + 2) * (2 div 1) mod 3"), 2);
 -	CHECK_XPATH_NUMBER(c, STR("2 - -2"), 4);
 -	CHECK_XPATH_NUMBER(c, STR("2 + -2"), 0);
 -	CHECK_XPATH_NUMBER(c, STR("2--2"), 4);
 -	CHECK_XPATH_NUMBER(c, STR("2+-2"), 0);
 -	CHECK_XPATH_NUMBER(c, STR("1-2-3"), -4);
 -
 -	// mod, from W3C standard
 -	CHECK_XPATH_NUMBER(c, STR("5 mod 2"), 1);
 -	CHECK_XPATH_NUMBER(c, STR("5 mod -2"), 1);
 -	CHECK_XPATH_NUMBER(c, STR("-5 mod 2"), -1);
 -	CHECK_XPATH_NUMBER(c, STR("-5 mod -2"), -1);
 -}
 -
 -TEST(xpath_operators_arithmetic_specials)
 -{
 -	xml_node c;
 -
 -	// infinity/nan
 -	CHECK_XPATH_STRING(c, STR("1 div 0"), STR("Infinity"));
 -	CHECK_XPATH_STRING(c, STR("-1 div 0"), STR("-Infinity"));
 -	CHECK_XPATH_STRING(c, STR("-1 div 0 + 1 div 0"), STR("NaN"));
 -	CHECK_XPATH_STRING(c, STR("0 div 0"), STR("NaN"));
 -	CHECK_XPATH_STRING(c, STR("1 div 0 + 1 div 0"), STR("Infinity"));
 -	CHECK_XPATH_STRING(c, STR("-1 div 0 + -1 div 0"), STR("-Infinity"));
 -	CHECK_XPATH_STRING(c, STR("1 div 0 + 100"), STR("Infinity"));
 -	CHECK_XPATH_STRING(c, STR("-1 div 0 + 100"), STR("-Infinity"));
 -	CHECK_XPATH_STRING(c, STR("0 div 0 + 100"), STR("NaN"));
 -	
 -	// unary - and multiplication clarifications from recommendations errata
 -	CHECK_XPATH_STRING(c, STR("1 div -0"), STR("-Infinity"));
 -	CHECK_XPATH_STRING(c, STR("-1 div -0"), STR("Infinity"));
 -	CHECK_XPATH_STRING(c, STR("1 div (-0 * 1)"), STR("-Infinity"));
 -	CHECK_XPATH_STRING(c, STR("-1 div (0 * -1)"), STR("Infinity"));
 -	CHECK_XPATH_STRING(c, STR("1 div (-0 div 1)"), STR("-Infinity"));
 -	CHECK_XPATH_STRING(c, STR("-1 div (0 div -1)"), STR("Infinity"));
 -}
 -
 -TEST_XML(xpath_operators_arithmetic_subtraction_parse, "<node><foo-bar>10</foo-bar><foo>2</foo><bar>3</bar></node>")
 -{
 -	xml_node n = doc.child(STR("node"));
 -
 -	// correct subtraction parsing, from W3C standard
 -	CHECK_XPATH_NUMBER(n, STR("foo-bar"), 10);
 -	CHECK_XPATH_NUMBER(n, STR("foo -bar"), -1);
 -	CHECK_XPATH_NUMBER(n, STR("foo - bar"), -1);
 -	CHECK_XPATH_NUMBER(n, STR("-foo-bar"), -10);
 -	CHECK_XPATH_NUMBER(n, STR("-foo -bar"), -5);
 -}
 -
 -TEST(xpath_operators_logical)
 -{
 -	xml_node c;
 -
 -	// boolean arithmetic
 -	CHECK_XPATH_BOOLEAN(c, STR("true() or true()"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("true() or false()"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("false() or false()"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("false() or true()"), true);
 -
 -	CHECK_XPATH_BOOLEAN(c, STR("true() and true()"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("true() and false()"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("false() and false()"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("false() and true()"), false);
 -	
 -	// boolean conversion
 -	CHECK_XPATH_BOOLEAN(c, STR("1 or ''"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("1 and ''"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("0 or ''"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("0 or 'a'"), true);
 -}
 -
 -TEST(xpath_operators_equality_primitive_boolean)
 -{
 -	xml_node c;
 -
 -	// boolean vs boolan
 -	CHECK_XPATH_BOOLEAN(c, STR("true() = true()"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("false() = false()"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("true() != false()"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("false() != false()"), false);
 -
 -	// upcast to boolean
 -	CHECK_XPATH_BOOLEAN(c, STR("true() = 2"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("true() != 2"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("false() = 2"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("false() != 2"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("false() = 0"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("false() != 0"), false);
 -
 -	CHECK_XPATH_BOOLEAN(c, STR("2 = true()"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("2 != true()"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("2 = false()"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("2 != false()"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("0 = false()"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("0 != false()"), false);
 -}
 -
 -TEST(xpath_operators_equality_primitive_number)
 -{
 -	xml_node c;
 -
 -	// number vs number
 -	CHECK_XPATH_BOOLEAN(c, STR("1 = 1"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("0.5 = 0.5"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("1 != 2"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("1 = -1"), false);
 -
 -	// infinity/nan
 -	CHECK_XPATH_BOOLEAN(c, STR("1 div 0 = 2 div 0"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("-1 div 0 != 2 div 0"), true);
 -
 -#ifndef MSVC6_NAN_BUG
 -	CHECK_XPATH_BOOLEAN(c, STR("0 div 0 = 1"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("0 div 0 != 1"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("0 div 0 = 0 div 0"), false);
 -#endif
 -
 -	// upcast to number
 -	CHECK_XPATH_BOOLEAN(c, STR("2 = '2'"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("2 != '2'"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("'1' != 2"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("'1' = 2"), false);
 -}
 -
 -TEST(xpath_operators_equality_primitive_string)
 -{
 -	xml_node c;
 -
 -	// string vs string
 -	CHECK_XPATH_BOOLEAN(c, STR("'a' = 'a'"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("'a' = 'b'"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("'ab' != 'a'"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("'' != 'a'"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("'a' != ''"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("'' != ''"), false);
 -}
 -
 -TEST_XML(xpath_operators_equality_node_set_node_set, "<node><c1><v>a</v><v>b</v></c1><c2><v>a</v><v>c</v></c2><c3><v>b</v></c3><c4><v>d</v></c4><c5><v>a</v><v>b</v></c5><c6><v>b</v></c6></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	// node set vs node set
 -	CHECK_XPATH_BOOLEAN(c, STR("x = x"), false); // empty node set compares as false with any other object via any comparison operator, as per XPath spec
 -	CHECK_XPATH_BOOLEAN(c, STR("x != x"), false);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v = c2/v"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v = c3/v"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("c2/v = c3/v"), false);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v = c4/v"), false);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v = x"), false);
 -	CHECK_XPATH_BOOLEAN(n, STR("x = c1"), false);
 -
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v != c2/v"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v != c3/v"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("c2/v != c3/v"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v != c4/v"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v != c5/v"), true); // (a, b) != (a, b), since a != b, as per XPath spec (comparison operators are so not intutive)
 -	CHECK_XPATH_BOOLEAN(n, STR("c3/v != c6/v"), false);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v != x"), false);
 -	CHECK_XPATH_BOOLEAN(n, STR("x != c1/v"), false);
 -}
 -
 -TEST_XML(xpath_operators_equality_node_set_primitive, "<node><c1><v>1</v><v>-1</v><v>100</v></c1><c2><v>1</v><v>nan</v></c2></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	// node set vs number
 -	CHECK_XPATH_BOOLEAN(c, STR("x = 1"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("x != 1"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("1 = x"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("1 != x"), false);
 -
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v = 1"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v = -1"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v != 1"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v = 5"), false);
 -	CHECK_XPATH_BOOLEAN(n, STR("c2/v = 1"), true);
 -
 -	CHECK_XPATH_BOOLEAN(n, STR("1 = c1/v"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("-1 = c1/v"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("1 != c1/v"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("5 = c1/v"), false);
 -	CHECK_XPATH_BOOLEAN(n, STR("1 = c2/v"), true);
 -
 -#ifndef MSVC6_NAN_BUG
 -	CHECK_XPATH_BOOLEAN(n, STR("c2/v != 1"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("1 != c2/v"), true);
 -#endif
 -	
 -	// node set vs string
 -	CHECK_XPATH_BOOLEAN(c, STR("x = '1'"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("x != '1'"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("'1' = x"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("'1' != x"), false);
 -
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v = '1'"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v = '-1'"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v != '1'"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v = '5'"), false);
 -	CHECK_XPATH_BOOLEAN(n, STR("c2/v = '1'"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("c2/v != '1'"), true);
 -
 -	CHECK_XPATH_BOOLEAN(n, STR("'1' = c1/v"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("'-1' = c1/v"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("'1' != c1/v"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("'5' = c1/v"), false);
 -	CHECK_XPATH_BOOLEAN(n, STR("'1' = c2/v"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("'1' != c2/v"), true);
 -
 -	// node set vs almost-numeric string just in case
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v = '1.0'"), false);
 -
 -	// node set vs boolean - special rules! empty sets are equal to true()
 -	CHECK_XPATH_BOOLEAN(n, STR("x = true()"), false);
 -	CHECK_XPATH_BOOLEAN(n, STR("x != true()"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("x = false()"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v = true()"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v != true()"), false);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v = false()"), false);
 -
 -	CHECK_XPATH_BOOLEAN(n, STR("true() = x"), false);
 -	CHECK_XPATH_BOOLEAN(n, STR("true() != x"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("false() = x"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("true() = c1/v"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("true() != c1/v"), false);
 -	CHECK_XPATH_BOOLEAN(n, STR("false() = c1/v"), false);
 -}
 -
 -TEST(xpath_operators_inequality_primitive)
 -{
 -	xml_node c;
 -
 -	// number vs number
 -	CHECK_XPATH_BOOLEAN(c, STR("1 < 2"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("1 <= 2"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("1 > 2"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("1 >= 2"), false);
 -
 -	CHECK_XPATH_BOOLEAN(c, STR("1 < 1"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("1 <= 1"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("1 > 1"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("1 >= 1"), true);
 -
 -	// infinity/nan
 -	CHECK_XPATH_BOOLEAN(c, STR("1 div 0 <= 2 div 0"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("1 div 0 < 2 div 0"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("-1 div 0 < 2 div 0"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("-1 div 0 > 2 div 0"), false);
 -
 -#ifndef MSVC6_NAN_BUG
 -	CHECK_XPATH_BOOLEAN(c, STR("0 div 0 < 1"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("0 div 0 <= 1"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("0 div 0 > 1"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("0 div 0 >= 1"), false);
 -#endif
 -
 -	// upcast to number
 -	CHECK_XPATH_BOOLEAN(c, STR("2 < '2'"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("1 < '2'"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("2 <= '2'"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("3 <= '2'"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("2 > '2'"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("3 > '2'"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("2 >= '2'"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("3 >= '2'"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("1 >= true()"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("1 > true()"), false);
 -}
 -
 -TEST_XML(xpath_operators_inequality_node_set_node_set, "<node><c1><v>1</v><v>-1</v><v>-100</v></c1><c2><v>1</v><v>nan</v></c2><c3><v>1</v><v>-4</v></c3></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	// node set vs node set
 -	CHECK_XPATH_BOOLEAN(c, STR("x < x"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("x > x"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("x <= x"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("x >= x"), false);
 -
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v > x"), false);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v < x"), false);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v >= x"), false);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v <= x"), false);
 -
 -	CHECK_XPATH_BOOLEAN(n, STR("x > c1/v"), false);
 -	CHECK_XPATH_BOOLEAN(n, STR("x < c1/v"), false);
 -	CHECK_XPATH_BOOLEAN(n, STR("x >= c1/v"), false);
 -	CHECK_XPATH_BOOLEAN(n, STR("x <= c1/v"), false);
 -
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v > c3/v"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v >= c3/v"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v < c3/v"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v <= c3/v"), true);
 -
 -#ifndef MSVC6_NAN_BUG
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v > c2/v"), false);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v >= c2/v"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v < c2/v"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v <= c2/v"), true);
 -#endif
 -}
 -
 -TEST_XML(xpath_operators_inequality_node_set_primitive, "<node><c1><v>1</v><v>-1</v><v>-100</v></c1><c2><v>1</v><v>nan</v></c2></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	// node set vs number
 -	CHECK_XPATH_BOOLEAN(c, STR("x < 0"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("x > 0"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("x <= 0"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("x >= 0"), false);
 -
 -	CHECK_XPATH_BOOLEAN(c, STR("0 < x"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("0 > x"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("0 <= x"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("0 >= x"), false);
 -
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v > 0"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v > 1"), false);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v >= 0"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v < 0"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v <= 0"), true);
 -
 -	CHECK_XPATH_BOOLEAN(n, STR("0 < c1/v"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("1 < c1/v"), false);
 -	CHECK_XPATH_BOOLEAN(n, STR("0 <= c1/v"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("0 > c1/v"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("0 >= c1/v"), true);
 -
 -	// node set vs string
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v > '0'"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v > '1'"), false);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v >= '0'"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v < '0'"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v <= '0'"), true);
 -
 -	CHECK_XPATH_BOOLEAN(n, STR("'0' < c1/v"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("'1' < c1/v"), false);
 -	CHECK_XPATH_BOOLEAN(n, STR("'0' <= c1/v"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("'0' > c1/v"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("'0' >= c1/v"), true);
 -
 -	// node set vs boolean
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v > false()"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v > true()"), false);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v >= false()"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v < false()"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("c1/v <= false()"), true);
 -
 -	CHECK_XPATH_BOOLEAN(n, STR("false() < c1/v"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("true() < c1/v"), false);
 -	CHECK_XPATH_BOOLEAN(n, STR("false() <= c1/v"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("false() > c1/v"), true);
 -	CHECK_XPATH_BOOLEAN(n, STR("false() >= c1/v"), true);
 -}
 -
 -TEST(xpath_operators_boolean_precedence)
 -{
 -	xml_node c;
 -
 -	CHECK_XPATH_BOOLEAN(c, STR("1 = 0 or 2 = 2"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("1 = (0 or 2) = false()"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("1 < 0 or 2 > 2"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("2 < 1 = false()"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("2 < (1 = false())"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("3 > 2 > 1"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("(3 > 2) > 1"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("3 > (2 > 1)"), true);
 -}
 -
 -TEST_XML(xpath_operators_union, "<node><employee/><employee secretary=''/><employee assistant=''/><employee secretary='' assistant=''/><employee assistant='' secretary=''/><tail/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(n, STR("employee | .")) % 2 % 3 % 4 % 6 % 8 % 11;
 -	CHECK_XPATH_NODESET(n, STR("employee[@secretary] | employee[@assistant]")) % 4 % 6 % 8 % 11;
 -	CHECK_XPATH_NODESET(n, STR("employee[@assistant] | employee[@secretary]")) % 4 % 6 % 8 % 11;
 -	CHECK_XPATH_NODESET(n, STR("employee[@secretary] | employee[@nobody]")) % 4 % 8 % 11;
 -	CHECK_XPATH_NODESET(n, STR("employee[@nobody] | employee[@secretary]")) % 4 % 8 % 11;
 -	CHECK_XPATH_NODESET(n, STR("tail/preceding-sibling::employee | .")) % 2 % 3 % 4 % 6 % 8 % 11;
 -	CHECK_XPATH_NODESET(n, STR(". | tail/preceding-sibling::employee | .")) % 2 % 3 % 4 % 6 % 8 % 11;
 -}
 -
 -TEST(xpath_operators_union_error)
 -{
 -	CHECK_XPATH_FAIL(STR(". | true()"));
 -	CHECK_XPATH_FAIL(STR(". | 1"));
 -	CHECK_XPATH_FAIL(STR(". | '1'"));
 -	CHECK_XPATH_FAIL(STR(". | count(.)"));
 -	CHECK_XPATH_FAIL(STR("true() | ."));
 -	CHECK_XPATH_FAIL(STR("1 | ."));
 -	CHECK_XPATH_FAIL(STR("'1' | ."));
 -	CHECK_XPATH_FAIL(STR("count(.) | ."));
 -}
 -
 -TEST(xpath_operators_associativity_boolean)
 -{
 -	xml_node c;
 -
 -	CHECK_XPATH_BOOLEAN(c, STR("false() or true() and true() and false()"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("3 > 2 > 1"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("4 > 3 > 2 > 1"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("5 > 4 > 3 > 2 > 1"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("1 < 2 < 3 < 4 < 5"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("1 <= 2 <= 3 <= 4 <= 5"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("5 >= 4 >= 3 >= 2 >= 1"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("3 >= 2 >= 1"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("2 >= 1"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("4 >= 3 >= 2 >= 1"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("((((5 > 4) > 3) > 2) > 1)"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("2 != 3 != 1 != 4 != 0"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("(((2 != 3) != 1) != 4) != 0"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("2 != 3 != 1 != 4 != 1"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("(((2 != 3) != 1) != 4) != 1"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("2 = 3 = 1 = 4 = 0"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("(((2 = 3) = 1) = 4) = 0"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("2 = 3 = 1 = 4 = 1"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("(((2 = 3) = 1) = 4) = 1"), false);
 -}
 -
 -TEST(xpath_operators_associativity_arithmetic)
 -{
 -	xml_node c;
 -
 -	CHECK_XPATH_NUMBER(c, STR("2+1-1+1"), 3);
 -	CHECK_XPATH_NUMBER(c, STR("1+2+1-1+1"), 4);
 -	CHECK_XPATH_NUMBER(c, STR("1+1+2+1-1+1"), 5);
 -	CHECK_XPATH_NUMBER(c, STR("1-1+1"), 1);
 -}
 -
 -#endif
 +#ifndef PUGIXML_NO_XPATH + +#include "common.hpp" + +TEST(xpath_operators_arithmetic) +{ +	xml_node c; + +	// incorrect unary operator +	CHECK_XPATH_FAIL(STR("-")); + +	// correct unary operator +	CHECK_XPATH_NUMBER(c, STR("-1"), -1); +	CHECK_XPATH_NUMBER(c, STR("--1"), 1); +	CHECK_XPATH_NUMBER(c, STR("---1"), -1); + +	// incorrect binary operators +	CHECK_XPATH_FAIL(STR("5+")); +	CHECK_XPATH_FAIL(STR("5-")); +	CHECK_XPATH_FAIL(STR("5*")); +	CHECK_XPATH_FAIL(STR("+5")); +	CHECK_XPATH_FAIL(STR("*5")); +	CHECK_XPATH_FAIL(STR("1div2")); +	CHECK_XPATH_FAIL(STR("1mod")); +	CHECK_XPATH_FAIL(STR("1div")); + +	// correct trivial binary operators +	CHECK_XPATH_NUMBER(c, STR("1 + 2"), 3); +	CHECK_XPATH_NUMBER(c, STR("1+2"), 3); +	CHECK_XPATH_NUMBER(c, STR("1 * 2"), 2); +	CHECK_XPATH_NUMBER(c, STR("1*2"), 2); +	CHECK_XPATH_NUMBER(c, STR("1 div 2"), 0.5); + +	// operator precedence +	CHECK_XPATH_NUMBER(c, STR("2 + 2 * 2 div 1 mod 3"), 3); +	CHECK_XPATH_NUMBER(c, STR("2 + 2 * 2 div (1 mod 3)"), 6); +	CHECK_XPATH_NUMBER(c, STR("(2 + 2) * 2 div (1 mod 3)"), 8); +	CHECK_XPATH_NUMBER(c, STR("(2 + 2) * (2 div 1) mod 3"), 2); +	CHECK_XPATH_NUMBER(c, STR("2 - -2"), 4); +	CHECK_XPATH_NUMBER(c, STR("2 + -2"), 0); +	CHECK_XPATH_NUMBER(c, STR("2--2"), 4); +	CHECK_XPATH_NUMBER(c, STR("2+-2"), 0); +	CHECK_XPATH_NUMBER(c, STR("1-2-3"), -4); + +	// mod, from W3C standard +	CHECK_XPATH_NUMBER(c, STR("5 mod 2"), 1); +	CHECK_XPATH_NUMBER(c, STR("5 mod -2"), 1); +	CHECK_XPATH_NUMBER(c, STR("-5 mod 2"), -1); +	CHECK_XPATH_NUMBER(c, STR("-5 mod -2"), -1); +} + +TEST(xpath_operators_arithmetic_specials) +{ +	xml_node c; + +	// infinity/nan +	CHECK_XPATH_STRING(c, STR("1 div 0"), STR("Infinity")); +	CHECK_XPATH_STRING(c, STR("-1 div 0"), STR("-Infinity")); +	CHECK_XPATH_STRING(c, STR("-1 div 0 + 1 div 0"), STR("NaN")); +	CHECK_XPATH_STRING(c, STR("0 div 0"), STR("NaN")); +	CHECK_XPATH_STRING(c, STR("1 div 0 + 1 div 0"), STR("Infinity")); +	CHECK_XPATH_STRING(c, STR("-1 div 0 + -1 div 0"), STR("-Infinity")); +	CHECK_XPATH_STRING(c, STR("1 div 0 + 100"), STR("Infinity")); +	CHECK_XPATH_STRING(c, STR("-1 div 0 + 100"), STR("-Infinity")); +	CHECK_XPATH_STRING(c, STR("0 div 0 + 100"), STR("NaN")); +	 +	// unary - and multiplication clarifications from recommendations errata +	CHECK_XPATH_STRING(c, STR("1 div -0"), STR("-Infinity")); +	CHECK_XPATH_STRING(c, STR("-1 div -0"), STR("Infinity")); +	CHECK_XPATH_STRING(c, STR("1 div (-0 * 1)"), STR("-Infinity")); +	CHECK_XPATH_STRING(c, STR("-1 div (0 * -1)"), STR("Infinity")); +	CHECK_XPATH_STRING(c, STR("1 div (-0 div 1)"), STR("-Infinity")); +	CHECK_XPATH_STRING(c, STR("-1 div (0 div -1)"), STR("Infinity")); +} + +TEST_XML(xpath_operators_arithmetic_subtraction_parse, "<node><foo-bar>10</foo-bar><foo>2</foo><bar>3</bar></node>") +{ +	xml_node n = doc.child(STR("node")); + +	// correct subtraction parsing, from W3C standard +	CHECK_XPATH_NUMBER(n, STR("foo-bar"), 10); +	CHECK_XPATH_NUMBER(n, STR("foo -bar"), -1); +	CHECK_XPATH_NUMBER(n, STR("foo - bar"), -1); +	CHECK_XPATH_NUMBER(n, STR("-foo-bar"), -10); +	CHECK_XPATH_NUMBER(n, STR("-foo -bar"), -5); +} + +TEST(xpath_operators_logical) +{ +	xml_node c; + +	// boolean arithmetic +	CHECK_XPATH_BOOLEAN(c, STR("true() or true()"), true); +	CHECK_XPATH_BOOLEAN(c, STR("true() or false()"), true); +	CHECK_XPATH_BOOLEAN(c, STR("false() or false()"), false); +	CHECK_XPATH_BOOLEAN(c, STR("false() or true()"), true); + +	CHECK_XPATH_BOOLEAN(c, STR("true() and true()"), true); +	CHECK_XPATH_BOOLEAN(c, STR("true() and false()"), false); +	CHECK_XPATH_BOOLEAN(c, STR("false() and false()"), false); +	CHECK_XPATH_BOOLEAN(c, STR("false() and true()"), false); +	 +	// boolean conversion +	CHECK_XPATH_BOOLEAN(c, STR("1 or ''"), true); +	CHECK_XPATH_BOOLEAN(c, STR("1 and ''"), false); +	CHECK_XPATH_BOOLEAN(c, STR("0 or ''"), false); +	CHECK_XPATH_BOOLEAN(c, STR("0 or 'a'"), true); +} + +TEST(xpath_operators_equality_primitive_boolean) +{ +	xml_node c; + +	// boolean vs boolan +	CHECK_XPATH_BOOLEAN(c, STR("true() = true()"), true); +	CHECK_XPATH_BOOLEAN(c, STR("false() = false()"), true); +	CHECK_XPATH_BOOLEAN(c, STR("true() != false()"), true); +	CHECK_XPATH_BOOLEAN(c, STR("false() != false()"), false); + +	// upcast to boolean +	CHECK_XPATH_BOOLEAN(c, STR("true() = 2"), true); +	CHECK_XPATH_BOOLEAN(c, STR("true() != 2"), false); +	CHECK_XPATH_BOOLEAN(c, STR("false() = 2"), false); +	CHECK_XPATH_BOOLEAN(c, STR("false() != 2"), true); +	CHECK_XPATH_BOOLEAN(c, STR("false() = 0"), true); +	CHECK_XPATH_BOOLEAN(c, STR("false() != 0"), false); + +	CHECK_XPATH_BOOLEAN(c, STR("2 = true()"), true); +	CHECK_XPATH_BOOLEAN(c, STR("2 != true()"), false); +	CHECK_XPATH_BOOLEAN(c, STR("2 = false()"), false); +	CHECK_XPATH_BOOLEAN(c, STR("2 != false()"), true); +	CHECK_XPATH_BOOLEAN(c, STR("0 = false()"), true); +	CHECK_XPATH_BOOLEAN(c, STR("0 != false()"), false); +} + +TEST(xpath_operators_equality_primitive_number) +{ +	xml_node c; + +	// number vs number +	CHECK_XPATH_BOOLEAN(c, STR("1 = 1"), true); +	CHECK_XPATH_BOOLEAN(c, STR("0.5 = 0.5"), true); +	CHECK_XPATH_BOOLEAN(c, STR("1 != 2"), true); +	CHECK_XPATH_BOOLEAN(c, STR("1 = -1"), false); + +	// infinity/nan +	CHECK_XPATH_BOOLEAN(c, STR("1 div 0 = 2 div 0"), true); +	CHECK_XPATH_BOOLEAN(c, STR("-1 div 0 != 2 div 0"), true); + +#ifndef MSVC6_NAN_BUG +	CHECK_XPATH_BOOLEAN(c, STR("0 div 0 = 1"), false); +	CHECK_XPATH_BOOLEAN(c, STR("0 div 0 != 1"), true); +	CHECK_XPATH_BOOLEAN(c, STR("0 div 0 = 0 div 0"), false); +#endif + +	// upcast to number +	CHECK_XPATH_BOOLEAN(c, STR("2 = '2'"), true); +	CHECK_XPATH_BOOLEAN(c, STR("2 != '2'"), false); +	CHECK_XPATH_BOOLEAN(c, STR("'1' != 2"), true); +	CHECK_XPATH_BOOLEAN(c, STR("'1' = 2"), false); +} + +TEST(xpath_operators_equality_primitive_string) +{ +	xml_node c; + +	// string vs string +	CHECK_XPATH_BOOLEAN(c, STR("'a' = 'a'"), true); +	CHECK_XPATH_BOOLEAN(c, STR("'a' = 'b'"), false); +	CHECK_XPATH_BOOLEAN(c, STR("'ab' != 'a'"), true); +	CHECK_XPATH_BOOLEAN(c, STR("'' != 'a'"), true); +	CHECK_XPATH_BOOLEAN(c, STR("'a' != ''"), true); +	CHECK_XPATH_BOOLEAN(c, STR("'' != ''"), false); +} + +TEST_XML(xpath_operators_equality_node_set_node_set, "<node><c1><v>a</v><v>b</v></c1><c2><v>a</v><v>c</v></c2><c3><v>b</v></c3><c4><v>d</v></c4><c5><v>a</v><v>b</v></c5><c6><v>b</v></c6></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	// node set vs node set +	CHECK_XPATH_BOOLEAN(c, STR("x = x"), false); // empty node set compares as false with any other object via any comparison operator, as per XPath spec +	CHECK_XPATH_BOOLEAN(c, STR("x != x"), false); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v = c2/v"), true); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v = c3/v"), true); +	CHECK_XPATH_BOOLEAN(n, STR("c2/v = c3/v"), false); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v = c4/v"), false); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v = x"), false); +	CHECK_XPATH_BOOLEAN(n, STR("x = c1"), false); + +	CHECK_XPATH_BOOLEAN(n, STR("c1/v != c2/v"), true); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v != c3/v"), true); +	CHECK_XPATH_BOOLEAN(n, STR("c2/v != c3/v"), true); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v != c4/v"), true); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v != c5/v"), true); // (a, b) != (a, b), since a != b, as per XPath spec (comparison operators are so not intutive) +	CHECK_XPATH_BOOLEAN(n, STR("c3/v != c6/v"), false); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v != x"), false); +	CHECK_XPATH_BOOLEAN(n, STR("x != c1/v"), false); +} + +TEST_XML(xpath_operators_equality_node_set_primitive, "<node><c1><v>1</v><v>-1</v><v>100</v></c1><c2><v>1</v><v>nan</v></c2></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	// node set vs number +	CHECK_XPATH_BOOLEAN(c, STR("x = 1"), false); +	CHECK_XPATH_BOOLEAN(c, STR("x != 1"), false); +	CHECK_XPATH_BOOLEAN(c, STR("1 = x"), false); +	CHECK_XPATH_BOOLEAN(c, STR("1 != x"), false); + +	CHECK_XPATH_BOOLEAN(n, STR("c1/v = 1"), true); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v = -1"), true); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v != 1"), true); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v = 5"), false); +	CHECK_XPATH_BOOLEAN(n, STR("c2/v = 1"), true); + +	CHECK_XPATH_BOOLEAN(n, STR("1 = c1/v"), true); +	CHECK_XPATH_BOOLEAN(n, STR("-1 = c1/v"), true); +	CHECK_XPATH_BOOLEAN(n, STR("1 != c1/v"), true); +	CHECK_XPATH_BOOLEAN(n, STR("5 = c1/v"), false); +	CHECK_XPATH_BOOLEAN(n, STR("1 = c2/v"), true); + +#ifndef MSVC6_NAN_BUG +	CHECK_XPATH_BOOLEAN(n, STR("c2/v != 1"), true); +	CHECK_XPATH_BOOLEAN(n, STR("1 != c2/v"), true); +#endif +	 +	// node set vs string +	CHECK_XPATH_BOOLEAN(c, STR("x = '1'"), false); +	CHECK_XPATH_BOOLEAN(c, STR("x != '1'"), false); +	CHECK_XPATH_BOOLEAN(c, STR("'1' = x"), false); +	CHECK_XPATH_BOOLEAN(c, STR("'1' != x"), false); + +	CHECK_XPATH_BOOLEAN(n, STR("c1/v = '1'"), true); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v = '-1'"), true); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v != '1'"), true); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v = '5'"), false); +	CHECK_XPATH_BOOLEAN(n, STR("c2/v = '1'"), true); +	CHECK_XPATH_BOOLEAN(n, STR("c2/v != '1'"), true); + +	CHECK_XPATH_BOOLEAN(n, STR("'1' = c1/v"), true); +	CHECK_XPATH_BOOLEAN(n, STR("'-1' = c1/v"), true); +	CHECK_XPATH_BOOLEAN(n, STR("'1' != c1/v"), true); +	CHECK_XPATH_BOOLEAN(n, STR("'5' = c1/v"), false); +	CHECK_XPATH_BOOLEAN(n, STR("'1' = c2/v"), true); +	CHECK_XPATH_BOOLEAN(n, STR("'1' != c2/v"), true); + +	// node set vs almost-numeric string just in case +	CHECK_XPATH_BOOLEAN(n, STR("c1/v = '1.0'"), false); + +	// node set vs boolean - special rules! empty sets are equal to true() +	CHECK_XPATH_BOOLEAN(n, STR("x = true()"), false); +	CHECK_XPATH_BOOLEAN(n, STR("x != true()"), true); +	CHECK_XPATH_BOOLEAN(n, STR("x = false()"), true); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v = true()"), true); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v != true()"), false); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v = false()"), false); + +	CHECK_XPATH_BOOLEAN(n, STR("true() = x"), false); +	CHECK_XPATH_BOOLEAN(n, STR("true() != x"), true); +	CHECK_XPATH_BOOLEAN(n, STR("false() = x"), true); +	CHECK_XPATH_BOOLEAN(n, STR("true() = c1/v"), true); +	CHECK_XPATH_BOOLEAN(n, STR("true() != c1/v"), false); +	CHECK_XPATH_BOOLEAN(n, STR("false() = c1/v"), false); +} + +TEST(xpath_operators_inequality_primitive) +{ +	xml_node c; + +	// number vs number +	CHECK_XPATH_BOOLEAN(c, STR("1 < 2"), true); +	CHECK_XPATH_BOOLEAN(c, STR("1 <= 2"), true); +	CHECK_XPATH_BOOLEAN(c, STR("1 > 2"), false); +	CHECK_XPATH_BOOLEAN(c, STR("1 >= 2"), false); + +	CHECK_XPATH_BOOLEAN(c, STR("1 < 1"), false); +	CHECK_XPATH_BOOLEAN(c, STR("1 <= 1"), true); +	CHECK_XPATH_BOOLEAN(c, STR("1 > 1"), false); +	CHECK_XPATH_BOOLEAN(c, STR("1 >= 1"), true); + +	// infinity/nan +	CHECK_XPATH_BOOLEAN(c, STR("1 div 0 <= 2 div 0"), true); +	CHECK_XPATH_BOOLEAN(c, STR("1 div 0 < 2 div 0"), false); +	CHECK_XPATH_BOOLEAN(c, STR("-1 div 0 < 2 div 0"), true); +	CHECK_XPATH_BOOLEAN(c, STR("-1 div 0 > 2 div 0"), false); + +#ifndef MSVC6_NAN_BUG +	CHECK_XPATH_BOOLEAN(c, STR("0 div 0 < 1"), false); +	CHECK_XPATH_BOOLEAN(c, STR("0 div 0 <= 1"), false); +	CHECK_XPATH_BOOLEAN(c, STR("0 div 0 > 1"), false); +	CHECK_XPATH_BOOLEAN(c, STR("0 div 0 >= 1"), false); +#endif + +	// upcast to number +	CHECK_XPATH_BOOLEAN(c, STR("2 < '2'"), false); +	CHECK_XPATH_BOOLEAN(c, STR("1 < '2'"), true); +	CHECK_XPATH_BOOLEAN(c, STR("2 <= '2'"), true); +	CHECK_XPATH_BOOLEAN(c, STR("3 <= '2'"), false); +	CHECK_XPATH_BOOLEAN(c, STR("2 > '2'"), false); +	CHECK_XPATH_BOOLEAN(c, STR("3 > '2'"), true); +	CHECK_XPATH_BOOLEAN(c, STR("2 >= '2'"), true); +	CHECK_XPATH_BOOLEAN(c, STR("3 >= '2'"), true); +	CHECK_XPATH_BOOLEAN(c, STR("1 >= true()"), true); +	CHECK_XPATH_BOOLEAN(c, STR("1 > true()"), false); +} + +TEST_XML(xpath_operators_inequality_node_set_node_set, "<node><c1><v>1</v><v>-1</v><v>-100</v></c1><c2><v>1</v><v>nan</v></c2><c3><v>1</v><v>-4</v></c3></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	// node set vs node set +	CHECK_XPATH_BOOLEAN(c, STR("x < x"), false); +	CHECK_XPATH_BOOLEAN(c, STR("x > x"), false); +	CHECK_XPATH_BOOLEAN(c, STR("x <= x"), false); +	CHECK_XPATH_BOOLEAN(c, STR("x >= x"), false); + +	CHECK_XPATH_BOOLEAN(n, STR("c1/v > x"), false); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v < x"), false); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v >= x"), false); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v <= x"), false); + +	CHECK_XPATH_BOOLEAN(n, STR("x > c1/v"), false); +	CHECK_XPATH_BOOLEAN(n, STR("x < c1/v"), false); +	CHECK_XPATH_BOOLEAN(n, STR("x >= c1/v"), false); +	CHECK_XPATH_BOOLEAN(n, STR("x <= c1/v"), false); + +	CHECK_XPATH_BOOLEAN(n, STR("c1/v > c3/v"), true); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v >= c3/v"), true); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v < c3/v"), true); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v <= c3/v"), true); + +#ifndef MSVC6_NAN_BUG +	CHECK_XPATH_BOOLEAN(n, STR("c1/v > c2/v"), false); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v >= c2/v"), true); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v < c2/v"), true); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v <= c2/v"), true); +#endif +} + +TEST_XML(xpath_operators_inequality_node_set_primitive, "<node><c1><v>1</v><v>-1</v><v>-100</v></c1><c2><v>1</v><v>nan</v></c2></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	// node set vs number +	CHECK_XPATH_BOOLEAN(c, STR("x < 0"), false); +	CHECK_XPATH_BOOLEAN(c, STR("x > 0"), false); +	CHECK_XPATH_BOOLEAN(c, STR("x <= 0"), false); +	CHECK_XPATH_BOOLEAN(c, STR("x >= 0"), false); + +	CHECK_XPATH_BOOLEAN(c, STR("0 < x"), false); +	CHECK_XPATH_BOOLEAN(c, STR("0 > x"), false); +	CHECK_XPATH_BOOLEAN(c, STR("0 <= x"), false); +	CHECK_XPATH_BOOLEAN(c, STR("0 >= x"), false); + +	CHECK_XPATH_BOOLEAN(n, STR("c1/v > 0"), true); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v > 1"), false); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v >= 0"), true); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v < 0"), true); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v <= 0"), true); + +	CHECK_XPATH_BOOLEAN(n, STR("0 < c1/v"), true); +	CHECK_XPATH_BOOLEAN(n, STR("1 < c1/v"), false); +	CHECK_XPATH_BOOLEAN(n, STR("0 <= c1/v"), true); +	CHECK_XPATH_BOOLEAN(n, STR("0 > c1/v"), true); +	CHECK_XPATH_BOOLEAN(n, STR("0 >= c1/v"), true); + +	// node set vs string +	CHECK_XPATH_BOOLEAN(n, STR("c1/v > '0'"), true); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v > '1'"), false); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v >= '0'"), true); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v < '0'"), true); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v <= '0'"), true); + +	CHECK_XPATH_BOOLEAN(n, STR("'0' < c1/v"), true); +	CHECK_XPATH_BOOLEAN(n, STR("'1' < c1/v"), false); +	CHECK_XPATH_BOOLEAN(n, STR("'0' <= c1/v"), true); +	CHECK_XPATH_BOOLEAN(n, STR("'0' > c1/v"), true); +	CHECK_XPATH_BOOLEAN(n, STR("'0' >= c1/v"), true); + +	// node set vs boolean +	CHECK_XPATH_BOOLEAN(n, STR("c1/v > false()"), true); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v > true()"), false); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v >= false()"), true); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v < false()"), true); +	CHECK_XPATH_BOOLEAN(n, STR("c1/v <= false()"), true); + +	CHECK_XPATH_BOOLEAN(n, STR("false() < c1/v"), true); +	CHECK_XPATH_BOOLEAN(n, STR("true() < c1/v"), false); +	CHECK_XPATH_BOOLEAN(n, STR("false() <= c1/v"), true); +	CHECK_XPATH_BOOLEAN(n, STR("false() > c1/v"), true); +	CHECK_XPATH_BOOLEAN(n, STR("false() >= c1/v"), true); +} + +TEST(xpath_operators_boolean_precedence) +{ +	xml_node c; + +	CHECK_XPATH_BOOLEAN(c, STR("1 = 0 or 2 = 2"), true); +	CHECK_XPATH_BOOLEAN(c, STR("1 = (0 or 2) = false()"), false); +	CHECK_XPATH_BOOLEAN(c, STR("1 < 0 or 2 > 2"), false); +	CHECK_XPATH_BOOLEAN(c, STR("2 < 1 = false()"), true); +	CHECK_XPATH_BOOLEAN(c, STR("2 < (1 = false())"), false); +	CHECK_XPATH_BOOLEAN(c, STR("3 > 2 > 1"), false); +	CHECK_XPATH_BOOLEAN(c, STR("(3 > 2) > 1"), false); +	CHECK_XPATH_BOOLEAN(c, STR("3 > (2 > 1)"), true); +} + +TEST_XML(xpath_operators_union, "<node><employee/><employee secretary=''/><employee assistant=''/><employee secretary='' assistant=''/><employee assistant='' secretary=''/><tail/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(n, STR("employee | .")) % 2 % 3 % 4 % 6 % 8 % 11; +	CHECK_XPATH_NODESET(n, STR("employee[@secretary] | employee[@assistant]")) % 4 % 6 % 8 % 11; +	CHECK_XPATH_NODESET(n, STR("employee[@assistant] | employee[@secretary]")) % 4 % 6 % 8 % 11; +	CHECK_XPATH_NODESET(n, STR("employee[@secretary] | employee[@nobody]")) % 4 % 8 % 11; +	CHECK_XPATH_NODESET(n, STR("employee[@nobody] | employee[@secretary]")) % 4 % 8 % 11; +	CHECK_XPATH_NODESET(n, STR("tail/preceding-sibling::employee | .")) % 2 % 3 % 4 % 6 % 8 % 11; +	CHECK_XPATH_NODESET(n, STR(". | tail/preceding-sibling::employee | .")) % 2 % 3 % 4 % 6 % 8 % 11; +} + +TEST(xpath_operators_union_error) +{ +	CHECK_XPATH_FAIL(STR(". | true()")); +	CHECK_XPATH_FAIL(STR(". | 1")); +	CHECK_XPATH_FAIL(STR(". | '1'")); +	CHECK_XPATH_FAIL(STR(". | count(.)")); +	CHECK_XPATH_FAIL(STR("true() | .")); +	CHECK_XPATH_FAIL(STR("1 | .")); +	CHECK_XPATH_FAIL(STR("'1' | .")); +	CHECK_XPATH_FAIL(STR("count(.) | .")); +} + +TEST(xpath_operators_associativity_boolean) +{ +	xml_node c; + +	CHECK_XPATH_BOOLEAN(c, STR("false() or true() and true() and false()"), false); +	CHECK_XPATH_BOOLEAN(c, STR("3 > 2 > 1"), false); +	CHECK_XPATH_BOOLEAN(c, STR("4 > 3 > 2 > 1"), false); +	CHECK_XPATH_BOOLEAN(c, STR("5 > 4 > 3 > 2 > 1"), false); +	CHECK_XPATH_BOOLEAN(c, STR("1 < 2 < 3 < 4 < 5"), true); +	CHECK_XPATH_BOOLEAN(c, STR("1 <= 2 <= 3 <= 4 <= 5"), true); +	CHECK_XPATH_BOOLEAN(c, STR("5 >= 4 >= 3 >= 2 >= 1"), false); +	CHECK_XPATH_BOOLEAN(c, STR("3 >= 2 >= 1"), true); +	CHECK_XPATH_BOOLEAN(c, STR("2 >= 1"), true); +	CHECK_XPATH_BOOLEAN(c, STR("4 >= 3 >= 2 >= 1"), false); +	CHECK_XPATH_BOOLEAN(c, STR("((((5 > 4) > 3) > 2) > 1)"), false); +	CHECK_XPATH_BOOLEAN(c, STR("2 != 3 != 1 != 4 != 0"), true); +	CHECK_XPATH_BOOLEAN(c, STR("(((2 != 3) != 1) != 4) != 0"), true); +	CHECK_XPATH_BOOLEAN(c, STR("2 != 3 != 1 != 4 != 1"), false); +	CHECK_XPATH_BOOLEAN(c, STR("(((2 != 3) != 1) != 4) != 1"), false); +	CHECK_XPATH_BOOLEAN(c, STR("2 = 3 = 1 = 4 = 0"), true); +	CHECK_XPATH_BOOLEAN(c, STR("(((2 = 3) = 1) = 4) = 0"), true); +	CHECK_XPATH_BOOLEAN(c, STR("2 = 3 = 1 = 4 = 1"), false); +	CHECK_XPATH_BOOLEAN(c, STR("(((2 = 3) = 1) = 4) = 1"), false); +} + +TEST(xpath_operators_associativity_arithmetic) +{ +	xml_node c; + +	CHECK_XPATH_NUMBER(c, STR("2+1-1+1"), 3); +	CHECK_XPATH_NUMBER(c, STR("1+2+1-1+1"), 4); +	CHECK_XPATH_NUMBER(c, STR("1+1+2+1-1+1"), 5); +	CHECK_XPATH_NUMBER(c, STR("1-1+1"), 1); +} + +#endif diff --git a/tests/test_xpath_parse.cpp b/tests/test_xpath_parse.cpp index ceede22..8c08ef9 100644 --- a/tests/test_xpath_parse.cpp +++ b/tests/test_xpath_parse.cpp @@ -1,272 +1,272 @@ -#ifndef PUGIXML_NO_XPATH
 -
 -#include "common.hpp"
 -
 -#include <string>
 -
 -TEST(xpath_literal_parse)
 -{
 -	xml_node c;
 -	CHECK_XPATH_STRING(c, STR("'a\"b'"), STR("a\"b"));
 -	CHECK_XPATH_STRING(c, STR("\"a'b\""), STR("a'b"));
 -	CHECK_XPATH_STRING(c, STR("\"\""), STR(""));
 -	CHECK_XPATH_STRING(c, STR("\'\'"), STR(""));
 -}
 -
 -TEST(xpath_literal_error)
 -{
 -	CHECK_XPATH_FAIL(STR("\""));
 -	CHECK_XPATH_FAIL(STR("\"foo"));
 -	CHECK_XPATH_FAIL(STR("\'"));
 -	CHECK_XPATH_FAIL(STR("\'bar"));
 -}
 -
 -TEST(xpath_number_parse)
 -{
 -	xml_node c;
 -	CHECK_XPATH_NUMBER(c, STR("0"), 0);
 -	CHECK_XPATH_NUMBER(c, STR("123"), 123);
 -	CHECK_XPATH_NUMBER(c, STR("123.456"), 123.456);
 -	CHECK_XPATH_NUMBER(c, STR(".123"), 0.123);
 -	CHECK_XPATH_NUMBER(c, STR("123.4567890123456789012345"), 123.4567890123456789012345);
 -	CHECK_XPATH_NUMBER(c, STR("123."), 123);
 -}
 -
 -TEST(xpath_number_error)
 -{
 -	CHECK_XPATH_FAIL(STR("123a"));
 -	CHECK_XPATH_FAIL(STR("123.a"));
 -	CHECK_XPATH_FAIL(STR(".123a"));
 -}
 -
 -TEST(xpath_variables)
 -{
 -	CHECK_XPATH_FAIL(STR("$var")); // not implemented
 -	CHECK_XPATH_FAIL(STR("$1"));
 -}
 -
 -TEST(xpath_empty_expression)
 -{
 -	CHECK_XPATH_FAIL(STR(""));
 -}
 -
 -TEST(xpath_lexer_error)
 -{
 -	CHECK_XPATH_FAIL(STR("!"));
 -	CHECK_XPATH_FAIL(STR("&"));
 -}
 -
 -TEST(xpath_unmatched_braces)
 -{
 -	CHECK_XPATH_FAIL(STR("node["));
 -	CHECK_XPATH_FAIL(STR("node[1"));
 -	CHECK_XPATH_FAIL(STR("node[]]"));
 -	CHECK_XPATH_FAIL(STR("node("));
 -	CHECK_XPATH_FAIL(STR("node(()"));
 -	CHECK_XPATH_FAIL(STR("(node)[1"));
 -	CHECK_XPATH_FAIL(STR("(1"));
 -}
 -
 -TEST(xpath_incorrect_step)
 -{
 -	CHECK_XPATH_FAIL(STR("child::1"));
 -	CHECK_XPATH_FAIL(STR("something::*"));
 -	CHECK_XPATH_FAIL(STR("a::*"));
 -	CHECK_XPATH_FAIL(STR("c::*"));
 -	CHECK_XPATH_FAIL(STR("d::*"));
 -	CHECK_XPATH_FAIL(STR("f::*"));
 -	CHECK_XPATH_FAIL(STR("n::*"));
 -	CHECK_XPATH_FAIL(STR("p::*"));
 -}
 -
 -TEST(xpath_semantics_error)
 -{
 -	CHECK_XPATH_FAIL(STR("1[1]"));
 -	CHECK_XPATH_FAIL(STR("1 | 1"));
 -}
 -
 -TEST(xpath_semantics_posinv) // coverage for contains()
 -{
 -	xpath_query(STR("(node)[substring(1, 2, 3)]"));
 -	xpath_query(STR("(node)[concat(1, 2, 3, 4)]"));
 -	xpath_query(STR("(node)[count(foo)]"));
 -	xpath_query(STR("(node)[local-name()]"));
 -	xpath_query(STR("(node)[(node)[1]]"));
 -}
 -
 -TEST(xpath_parse_paths_valid)
 -{
 -    const char_t* paths[] =
 -	{
 -		// From Jaxen tests
 -		STR("foo[.='bar']"), STR("foo[.!='bar']"), STR("/"), STR("*"), STR("//foo"), STR("/*"), STR("/."), STR("/foo[/bar[/baz]]"),
 -		STR("/foo/bar/baz[(1 or 2) + 3 * 4 + 8 and 9]"), STR("/foo/bar/baz"), STR("(.)[1]"), STR("self::node()"), STR("."), STR("count(/)"),
 -		STR("foo[1]"), STR("/baz[(1 or 2) + 3 * 4 + 8 and 9]"), STR("foo/bar[/baz[(1 or 2) - 3 mod 4 + 8 and 9 div 8]]"),
 -		STR("foo/bar/yeah:baz[a/b/c and toast]"), STR("/foo/bar[../x='123']"), STR("/foo[@bar='1234']"), STR("foo|bar"),
 -		STR("/foo|/bar[@id='1234']"), STR("count(//author/attribute::*)"), STR("/child::node()/child::node()[@id='_13563275']"),
 -		STR("10 + (count(descendant::author) * 5)"), STR("10 + count(descendant::author) * 5"), STR("2 + (2 * 5)"), STR("//foo:bar"),
 -		STR("count(//author)+5"), STR("count(//author)+count(//author/attribute::*)"), STR("/foo/bar[@a='1' and @c!='2']"),
 -		STR("12 + (count(//author)+count(//author/attribute::*)) div 2"), STR("text()[.='foo']"), STR("/*/*[@id='123']")
 -        STR("/foo/bar[@a='1' and @b='2']"), STR("/foo/bar[@a='1' and @b!='2']"), STR("//attribute::*[.!='crunchy']"),
 -        STR("'//*[contains(string(text()),\"yada yada\")]'"),
 -
 -		// From ajaxslt tests
 -		STR("@*"), STR("@*|node()"), STR("/descendant-or-self::div"), STR("/div"), STR("//div"), STR("/descendant-or-self::node()/child::para"),
 -		STR("substring('12345', 0, 3)"), STR("//title | //link"), STR("x//title"), STR("x/title"), STR("id('a')//title"), STR("//*[@about]"),
 -		STR("count(descendant::*)"), STR("count(descendant::*) + count(ancestor::*)"), STR("@*|text()"), STR("*|/"), STR("source|destination"),
 -		STR("page != 'to' and page != 'from'"), STR("substring-after(icon/@image, '/mapfiles/marker')"), STR("substring-before(str, c)"), STR("page = 'from'"),
 -		STR("segments/@time"), STR("child::para"), STR("child::*"), STR("child::text()"), STR("child::node()"), STR("attribute::name"), STR("attribute::*"),
 -		STR("descendant::para"), STR("ancestor::div"), STR("ancestor-or-self::div"), STR("descendant-or-self::para"), STR("self::para"), STR("child::*/child::para"),
 -		STR("concat(substring-before(@image,'marker'),'icon',substring-after(@image,'marker'))"), STR("/"), STR("/descendant::para"), STR("/descendant::olist/child::item"),
 -		STR("child::para[position()=1]"), STR("child::para[position()=last()]"), STR("child::para[position()=last()-1]"), STR("child::para[position()>1]"),
 -		STR("following-sibling::chapter[position()=1]"), STR("preceding-sibling::chapter[position()=1]"), STR("/descendant::figure[position()=42]"),
 -		STR("/child::doc/child::chapter[position()=5]/child::section[position()=2]"), STR("child::chapter/descendant::para"), STR("child::para[attribute::type='warning']"),
 -		STR("child::para[attribute::type='warning'][position()=5]"), STR("child::para[position()=5][attribute::type='warning']"), STR("child::chapter[child::title='Introduction']"),
 -		STR("child::chapter[child::title]"), STR("child::*[self::chapter or self::appendix]"), STR("child::*[self::chapter or self::appendix][position()=last()]"),
 -		STR("count(//*[id='u1']|//*[id='u2'])"), STR("count(//*[id='u1']|//*[class='u'])"), STR("count(//*[class='u']|//*[class='u'])"), STR("count(//*[class='u']|//*[id='u1'])"),
 -		STR("count(//*[@id='self']/ancestor-or-self::*)"), STR("count(//*[@id='self']/ancestor::*)"), STR("count(//*[@id='self']/attribute::*)"), STR("count(//*[@id='self']/child::*)"),
 -		STR("count(//*[@id='self']/descendant-or-self::*)"), STR("count(//*[@id='self']/descendant::*)"), STR("count(//*[@id='self']/following-sibling::*)"),
 -		STR("count(//*[@id='self']/following::*)"), STR("//*[@id='self']/parent::*/@id"), STR("count(//*[@id='self']/preceding-sibling::*)"),
 -		STR("count(//*[@id='self']/preceding::*)"), STR("//*[@id='self']/self::*/@id"), STR("id('nested1')/div[1]//input[2]"), STR("id('foo')//div[contains(@id, 'useful')]//input"),
 -		STR("(//table[@class='stylee'])//th[text()='theHeaderText']/../td"), STR("address"), STR("address=string(/page/user/defaultlocation)"), STR("count-of-snippet-of-url = 0"),
 -		STR("daddr"), STR("form"), STR("form = 'from'"), STR("form = 'to'"), STR("form='near'"), STR("home"), STR("i"), STR("i > page and i < page + range"),
 -		STR("i < page and i >= page - range"), STR("i < @max"), STR("i <= page"), STR("i + 1"), STR("i = page"), STR("i = 1"), STR("info = position() or (not(info) and position() = 1)"),
 -		STR("is-first-order"), STR("is-first-order and snippets-exist"), STR("more"), STR("more > 0"), STR("near-point"), STR("page"), STR("page != 'from'"), STR("page != 'to'"),
 -		STR("page != 'to' and page != 'from'"), STR("page > 1"), STR("page = 'basics'"), STR("page = 'details'"), STR("page = 'from'"), STR("page = 'to'"), STR("page='from'"),
 -		STR("page='to'"), STR("r >= 0.5"), STR("r >= 1"), STR("r - 0"), STR("r - 1"), STR("r - 2"), STR("r - 3"), STR("r - 4"), STR("saddr"), STR("sources"), STR("sources[position() < details]"),
 -		STR("src"), STR("str"), STR("\"'\""), STR("(//location[string(info/references/reference[1]/url)=string(current-url)]/info/references/reference[1])[1]"),
 -		STR("(not(count-of-snippet-of-url = 0) and (position() = 1) or not(current-url = //locations/location[position() = last-pos]//reference[1]/url))"),
 -		STR("(not(info) and position() = 1) or info = position()"), STR("."), STR("../@arg0"), STR("../@filterpng"), STR("/page/@filterpng"), STR("4"), STR("@attribution"),
 -		STR("@id"), STR("@max > @num"), STR("@meters > 16093"), STR("@name"), STR("@start div @num + 1"), STR("@url"), STR("ad"), STR("address/line"), STR("adsmessage"),
 -		STR("attr"), STR("boolean(location[@id='near'][icon/@image])"), STR("bubble/node()"), STR("calltoaction/node()"), STR("category"), STR("contains(str, c)"),
 -		STR("count(//location[string(info/references/reference[1]/url)=string(current-url)]//snippet)"), STR("count(//snippet)"), STR("count(attr)"), STR("count(location)"),
 -		STR("count(structured/source) > 1"), STR("description/node()"), STR("destination"), STR("destinationAddress"), STR("domain"), STR("false()"), STR("icon/@class != 'noicon'"),
 -		STR("icon/@image"), STR("info"), STR("info/address/line"), STR("info/distance"), STR("info/distance and near-point"), STR("info/distance and info/phone and near-point"),
 -		STR("info/distance or info/phone"), STR("info/panel/node()"), STR("info/phone"), STR("info/references/reference[1]"), STR("info/references/reference[1]/snippet"),
 -		STR("info/references/reference[1]/url"), STR("info/title"), STR("info/title/node()"), STR("line"), STR("location"), STR("location[@id!='near']"), STR("location[@id='near'][icon/@image]"),
 -		STR("location[position() > umlocations div 2]"), STR("location[position() <= numlocations div 2]"), STR("locations"), STR("locations/location"), STR("near"), STR("node()"),
 -		STR("not(count-of-snippets = 0)"), STR("not(form = 'from')"), STR("not(form = 'near')"), STR("not(form = 'to')"), STR("not(../@page)"), STR("not(structured/source)"), STR("notice"),
 -		STR("number(../@info)"), STR("number(../@items)"), STR("number(/page/@linewidth)"), STR("page/ads"), STR("page/directions"), STR("page/error"), STR("page/overlay"),
 -		STR("page/overlay/locations/location"), STR("page/refinements"), STR("page/request/canonicalnear"), STR("page/request/near"), STR("page/request/query"), STR("page/spelling/suggestion"),
 -		STR("page/user/defaultlocation"), STR("phone"), STR("position()"), STR("position() != 1"), STR("position() != last()"), STR("position() > 1"), STR("position() < details"),
 -		STR("position()-1"), STR("query"), STR("references/@total"), STR("references/reference"), STR("references/reference/domain"), STR("references/reference/url"),
 -		STR("reviews/@positive div (reviews/@positive + reviews/@negative) * 5"), STR("reviews/@positive div (reviews/@positive + reviews/@negative) * (5)"), STR("reviews/@total"),
 -		STR("reviews/@total > 1"), STR("reviews/@total > 5"), STR("reviews/@total = 1"), STR("segments/@distance"), STR("segments/@time"), STR("segments/segment"), STR("shorttitle/node()"),
 -		STR("snippet"), STR("snippet/node()"), STR("source"), STR("sourceAddress"), STR("sourceAddress and destinationAddress"), STR("string(../@daddr)"), STR("string(../@form)"),
 -		STR("string(../@page)"), STR("string(../@saddr)"), STR("string(info/title)"), STR("string(page/request/canonicalnear) != ''"), STR("string(page/request/near) != ''"),
 -		STR("string-length(address) > linewidth"), STR("structured/@total - details"), STR("structured/source"), STR("structured/source[@name]"), STR("substring(address, 1, linewidth - 3)"),
 -		STR("substring-after(str, c)"), STR("substring-after(icon/@image, '/mapfiles/marker')"), STR("substring-before(str, c)"), STR("tagline/node()"), STR("targetedlocation"),
 -		STR("title"), STR("title/node()"), STR("true()"), STR("url"), STR("visibleurl"), STR("id(\"level10\")/ancestor::SPAN"), STR("id(\"level10\")/ancestor-or-self::SPAN"), STR("//attribute::*"),
 -        STR("child::HTML/child::BODY/child::H1"), STR("descendant::node()"), STR("descendant-or-self::SPAN"), STR("id(\"first\")/following::text()"), STR("id(\"first\")/following-sibling::node()"),
 -        STR("id(\"level10\")/parent::node()"), STR("id(\"last\")/preceding::text()"), STR("id(\"last\")/preceding-sibling::node()"), STR("/HTML/BODY/H1/self::node()"), STR("//*[@name]"),
 -        STR("id(\"pet\")/SELECT[@name=\"species\"]/OPTION[@selected]/@value"), STR("descendant::INPUT[@name=\"name\"]/@value"), STR("id(\"pet\")/INPUT[@name=\"gender\" and @checked]/@value"),
 -        STR("//TEXTAREA[@name=\"description\"]/text()"), STR("id(\"div1\")|id(\"div2\")|id(\"div3 div4 div5\")"), STR("//LI[1]"), STR("//LI[last()]/text()"), STR("//LI[position() mod 2]/@class"),
 -        STR("//text()[.=\"foo\"]"), STR("descendant-or-self::SPAN[position() > 2]"), STR("descendant::*[contains(@class,\" fruit \")]"),
 -
 -		// ajaxslt considers this path invalid, however I believe it's valid as per spec
 -		STR("***"),
 -
 -		// Oasis MSFT considers this path invalid, however I believe it's valid as per spec
 -		STR("**..**"),
 -
 -		// Miscellaneous
 -		STR("..***..***.***.***..***..***..")
 -    };
 -
 -	for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i)
 -	{
 -		xpath_query q(paths[i]);
 -	}
 -}
 -
 -TEST(xpath_parse_paths_valid_unicode)
 -{
 -    // From ajaxslt
 -	const wchar_t* paths[] =
 -	{
 -	#ifdef U_LITERALS
 -		L"/descendant-or-self::\u90e8\u5206", L"//\u90e8\u5206", L"substring('\uff11\uff12\uff13\uff14\uff15', 0, 3)", L"//\u30bf\u30a4\u30c8\u30eb | //\u30ea\u30f3\u30af",
 -		L"\u8b0e//\u30bf\u30a4\u30c8\u30eb", L"//*[@\u30c7\u30b9\u30c6\u30a3\u30cd\u30a4\u30b7\u30e7\u30f3]", L"\u30da\u30fc\u30b8 = '\u304b\u3089'",
 -		L"concat(substring-before(@\u30a4\u30e1\u30fc\u30b8,'\u76ee\u5370'),'\u30a2\u30a4\u30b3\u30f3',substring-after(@\u30a4\u30e1\u30fc\u30b8,'\u76ee\u5370'))",
 -		L"\u30bd\u30fc\u30b9|\u30c7\u30b9\u30c6\u30a3\u30cd\u30a4\u30b7\u30e7\u30f3", L"\u30da\u30fc\u30b8 != '\u307e\u3067' and \u30da\u30fc\u30b8 != '\u304b\u3089'",
 -		L"substring-after(\u30a2\u30a4\u30b3\u30f3/@\u30a4\u30e1\u30fc\u30b8, '/\u5730\u56f3\u30d5\u30a1\u30a4\u30eb/\u76ee\u5370')", L"child::\u6bb5\u843d",
 -		L"substring-before(\u6587\u5b57\u5217, \u6587\u5b57)", L"\u30bb\u30b0\u30e1\u30f3\u30c8/@\u6642\u523b", L"attribute::\u540d\u524d", L"descendant::\u6bb5\u843d",
 -		L"ancestor::\u90e8\u5206", L"ancestor-or-self::\u90e8\u5206", L"descendant-or-self::\u6bb5\u843d", L"self::\u6bb5\u843d", L"child::\u7ae0/descendant::\u6bb5\u843d",
 -		L"child::*/child::\u6bb5\u843d", L"/descendant::\u6bb5\u843d", L"/descendant::\u9806\u5e8f\u30ea\u30b9\u30c8/child::\u9805\u76ee", L"child::\u6bb5\u843d[position()=1]",
 -		L"child::\u6bb5\u843d[position()=last()]", L"child::\u6bb5\u843d[position()=last()-1]", L"child::\u6bb5\u843d[position()>1]", L"following-sibling::\u7ae0[position()=1]",
 -		L"preceding-sibling::\u7ae0[position()=1]", L"/descendant::\u56f3\u8868[position()=42]", L"/child::\u6587\u66f8/child::\u7ae0[position()=5]/child::\u7bc0[position()=2]",
 -		L"child::\u6bb5\u843d[attribute::\u30bf\u30a4\u30d7='\u8b66\u544a']", L"child::\u6bb5\u843d[attribute::\u30bf\u30a4\u30d7='\u8b66\u544a'][position()=5]",
 -		L"child::\u6bb5\u843d[position()=5][attribute::\u30bf\u30a4\u30d7='\u8b66\u544a']", L"child::\u7ae0[child::\u30bf\u30a4\u30c8\u30eb='\u306f\u3058\u3081\u306b']",
 -		L"child::\u7ae0[child::\u30bf\u30a4\u30c8\u30eb]", L"child::*[self::\u7ae0 or self::\u4ed8\u9332]", L"child::*[self::\u7ae0 or self::\u4ed8\u9332][position()=last()]",
 -	#else
 -		L"/descendant-or-self::\x90e8\x5206", L"//\x90e8\x5206", L"substring('\xff11\xff12\xff13\xff14\xff15', 0, 3)", L"//\x30bf\x30a4\x30c8\x30eb | //\x30ea\x30f3\x30af",
 -		L"\x8b0e//\x30bf\x30a4\x30c8\x30eb", L"//*[@\x30c7\x30b9\x30c6\x30a3\x30cd\x30a4\x30b7\x30e7\x30f3]", L"\x30da\x30fc\x30b8 = '\x304b\x3089'",
 -		L"concat(substring-before(@\x30a4\x30e1\x30fc\x30b8,'\x76ee\x5370'),'\x30a2\x30a4\x30b3\x30f3',substring-after(@\x30a4\x30e1\x30fc\x30b8,'\x76ee\x5370'))",
 -		L"\x30bd\x30fc\x30b9|\x30c7\x30b9\x30c6\x30a3\x30cd\x30a4\x30b7\x30e7\x30f3", L"\x30da\x30fc\x30b8 != '\x307e\x3067' and \x30da\x30fc\x30b8 != '\x304b\x3089'",
 -		L"substring-after(\x30a2\x30a4\x30b3\x30f3/@\x30a4\x30e1\x30fc\x30b8, '/\x5730\x56f3\x30d5\x30a1\x30a4\x30eb/\x76ee\x5370')", L"child::\x6bb5\x843d",
 -		L"substring-before(\x6587\x5b57\x5217, \x6587\x5b57)", L"\x30bb\x30b0\x30e1\x30f3\x30c8/@\x6642\x523b", L"attribute::\x540d\x524d", L"descendant::\x6bb5\x843d",
 -		L"ancestor::\x90e8\x5206", L"ancestor-or-self::\x90e8\x5206", L"descendant-or-self::\x6bb5\x843d", L"self::\x6bb5\x843d", L"child::\x7ae0/descendant::\x6bb5\x843d",
 -		L"child::*/child::\x6bb5\x843d", L"/descendant::\x6bb5\x843d", L"/descendant::\x9806\x5e8f\x30ea\x30b9\x30c8/child::\x9805\x76ee", L"child::\x6bb5\x843d[position()=1]",
 -		L"child::\x6bb5\x843d[position()=last()]", L"child::\x6bb5\x843d[position()=last()-1]", L"child::\x6bb5\x843d[position()>1]", L"following-sibling::\x7ae0[position()=1]",
 -		L"preceding-sibling::\x7ae0[position()=1]", L"/descendant::\x56f3\x8868[position()=42]", L"/child::\x6587\x66f8/child::\x7ae0[position()=5]/child::\x7bc0[position()=2]",
 -		L"child::\x6bb5\x843d[attribute::\x30bf\x30a4\x30d7='\x8b66\x544a']", L"child::\x6bb5\x843d[attribute::\x30bf\x30a4\x30d7='\x8b66\x544a'][position()=5]",
 -		L"child::\x6bb5\x843d[position()=5][attribute::\x30bf\x30a4\x30d7='\x8b66\x544a']", L"child::\x7ae0[child::\x30bf\x30a4\x30c8\x30eb='\x306f\x3058\x3081\x306b']",
 -		L"child::\x7ae0[child::\x30bf\x30a4\x30c8\x30eb]", L"child::*[self::\x7ae0 or self::\x4ed8\x9332]", L"child::*[self::\x7ae0 or self::\x4ed8\x9332][position()=last()]",
 -	#endif
 -	};
 -
 -	for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i)
 -	{
 -	#if defined(PUGIXML_WCHAR_MODE)
 -		xpath_query q(paths[i]);
 -	#elif !defined(PUGIXML_NO_STL)
 -		std::basic_string<char> path_utf8 = pugi::as_utf8(paths[i]);
 -		xpath_query q(path_utf8.c_str());
 -	#endif
 -	}
 -}
 -
 -TEST(xpath_parse_invalid)
 -{
 -    const char_t* paths[] =
 -	{
 -		// From Jaxen tests
 -		STR("//:p"), STR("/foo/bar/"), STR("12 + (count(//author)+count(//author/attribute::*)) / 2"), STR("id()/2"), STR("+"),
 -		STR("///triple slash"), STR("/numbers numbers"), STR("/a/b[c > d]efg"), STR("/inv/child::"), STR("/invoice/@test[abcd"),
 -		STR("/invoice/@test[abcd > x"), STR("string-length('a"), STR("/descendant::()"), STR("(1 + 1"), STR("!false()"),
 -		STR("$author"), STR("10 + $foo"), STR("$foo:bar"), STR("$varname[@a='1']"), STR("foo/$variable/foo"),
 -		STR(".[1]"), STR("chyld::foo"), STR("foo/tacos()"), STR("foo/tacos()"), STR("/foo/bar[baz"), STR("//"), STR("*:foo"),
 -        STR("/cracker/cheese[(mold > 1) and (sense/taste"),
 -
 -		// From xpath-as3 tests
 -		STR("a b"), STR("//self::node())"), STR("/x/y[contains(self::node())"), STR("/x/y[contains(self::node()]"), STR("///"), STR("text::a"),
 -
 -		// From haXe-xpath tests
 -		STR("|/gjs"), STR("+3"), STR("/html/body/p != ---'div'/a"), STR(""), STR("@"), STR("#akf"), STR(",")
 -
 -		// Miscellaneous
 -		STR("..."), STR("...."), STR("**"), STR("****"), STR("******"), STR("..***..***.***.***..***..***..*"), STR("/[1]")
 -	};
 -
 -	for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i)
 -	{
 -		CHECK_XPATH_FAIL(paths[i]);
 -	}
 -}
 -
 -TEST_XML(xpath_parse_absolute, "<div><s/></div>")
 -{
 -	CHECK_XPATH_NODESET(doc, STR("/")) % 1;
 -
 -	CHECK_XPATH_NODESET(doc, STR("/div/s")) % 3;
 -	CHECK_XPATH_NODESET(doc, STR("/ div /s")) % 3;
 -	CHECK_XPATH_FAIL(STR("/ div 5"));
 -
 -	CHECK_XPATH_NODESET(doc, STR("/*/s")) % 3;
 -	CHECK_XPATH_NODESET(doc, STR("/ * /s")) % 3;
 -	CHECK_XPATH_FAIL(STR("/ * 5"));
 -
 -	CHECK_XPATH_NODESET(doc, STR("/*[/]")) % 2;
 -}
 -
 -#endif
 +#ifndef PUGIXML_NO_XPATH + +#include "common.hpp" + +#include <string> + +TEST(xpath_literal_parse) +{ +	xml_node c; +	CHECK_XPATH_STRING(c, STR("'a\"b'"), STR("a\"b")); +	CHECK_XPATH_STRING(c, STR("\"a'b\""), STR("a'b")); +	CHECK_XPATH_STRING(c, STR("\"\""), STR("")); +	CHECK_XPATH_STRING(c, STR("\'\'"), STR("")); +} + +TEST(xpath_literal_error) +{ +	CHECK_XPATH_FAIL(STR("\"")); +	CHECK_XPATH_FAIL(STR("\"foo")); +	CHECK_XPATH_FAIL(STR("\'")); +	CHECK_XPATH_FAIL(STR("\'bar")); +} + +TEST(xpath_number_parse) +{ +	xml_node c; +	CHECK_XPATH_NUMBER(c, STR("0"), 0); +	CHECK_XPATH_NUMBER(c, STR("123"), 123); +	CHECK_XPATH_NUMBER(c, STR("123.456"), 123.456); +	CHECK_XPATH_NUMBER(c, STR(".123"), 0.123); +	CHECK_XPATH_NUMBER(c, STR("123.4567890123456789012345"), 123.4567890123456789012345); +	CHECK_XPATH_NUMBER(c, STR("123."), 123); +} + +TEST(xpath_number_error) +{ +	CHECK_XPATH_FAIL(STR("123a")); +	CHECK_XPATH_FAIL(STR("123.a")); +	CHECK_XPATH_FAIL(STR(".123a")); +} + +TEST(xpath_variables) +{ +	CHECK_XPATH_FAIL(STR("$var")); // not implemented +	CHECK_XPATH_FAIL(STR("$1")); +} + +TEST(xpath_empty_expression) +{ +	CHECK_XPATH_FAIL(STR("")); +} + +TEST(xpath_lexer_error) +{ +	CHECK_XPATH_FAIL(STR("!")); +	CHECK_XPATH_FAIL(STR("&")); +} + +TEST(xpath_unmatched_braces) +{ +	CHECK_XPATH_FAIL(STR("node[")); +	CHECK_XPATH_FAIL(STR("node[1")); +	CHECK_XPATH_FAIL(STR("node[]]")); +	CHECK_XPATH_FAIL(STR("node(")); +	CHECK_XPATH_FAIL(STR("node(()")); +	CHECK_XPATH_FAIL(STR("(node)[1")); +	CHECK_XPATH_FAIL(STR("(1")); +} + +TEST(xpath_incorrect_step) +{ +	CHECK_XPATH_FAIL(STR("child::1")); +	CHECK_XPATH_FAIL(STR("something::*")); +	CHECK_XPATH_FAIL(STR("a::*")); +	CHECK_XPATH_FAIL(STR("c::*")); +	CHECK_XPATH_FAIL(STR("d::*")); +	CHECK_XPATH_FAIL(STR("f::*")); +	CHECK_XPATH_FAIL(STR("n::*")); +	CHECK_XPATH_FAIL(STR("p::*")); +} + +TEST(xpath_semantics_error) +{ +	CHECK_XPATH_FAIL(STR("1[1]")); +	CHECK_XPATH_FAIL(STR("1 | 1")); +} + +TEST(xpath_semantics_posinv) // coverage for contains() +{ +	xpath_query(STR("(node)[substring(1, 2, 3)]")); +	xpath_query(STR("(node)[concat(1, 2, 3, 4)]")); +	xpath_query(STR("(node)[count(foo)]")); +	xpath_query(STR("(node)[local-name()]")); +	xpath_query(STR("(node)[(node)[1]]")); +} + +TEST(xpath_parse_paths_valid) +{ +    const char_t* paths[] = +	{ +		// From Jaxen tests +		STR("foo[.='bar']"), STR("foo[.!='bar']"), STR("/"), STR("*"), STR("//foo"), STR("/*"), STR("/."), STR("/foo[/bar[/baz]]"), +		STR("/foo/bar/baz[(1 or 2) + 3 * 4 + 8 and 9]"), STR("/foo/bar/baz"), STR("(.)[1]"), STR("self::node()"), STR("."), STR("count(/)"), +		STR("foo[1]"), STR("/baz[(1 or 2) + 3 * 4 + 8 and 9]"), STR("foo/bar[/baz[(1 or 2) - 3 mod 4 + 8 and 9 div 8]]"), +		STR("foo/bar/yeah:baz[a/b/c and toast]"), STR("/foo/bar[../x='123']"), STR("/foo[@bar='1234']"), STR("foo|bar"), +		STR("/foo|/bar[@id='1234']"), STR("count(//author/attribute::*)"), STR("/child::node()/child::node()[@id='_13563275']"), +		STR("10 + (count(descendant::author) * 5)"), STR("10 + count(descendant::author) * 5"), STR("2 + (2 * 5)"), STR("//foo:bar"), +		STR("count(//author)+5"), STR("count(//author)+count(//author/attribute::*)"), STR("/foo/bar[@a='1' and @c!='2']"), +		STR("12 + (count(//author)+count(//author/attribute::*)) div 2"), STR("text()[.='foo']"), STR("/*/*[@id='123']") +        STR("/foo/bar[@a='1' and @b='2']"), STR("/foo/bar[@a='1' and @b!='2']"), STR("//attribute::*[.!='crunchy']"), +        STR("'//*[contains(string(text()),\"yada yada\")]'"), + +		// From ajaxslt tests +		STR("@*"), STR("@*|node()"), STR("/descendant-or-self::div"), STR("/div"), STR("//div"), STR("/descendant-or-self::node()/child::para"), +		STR("substring('12345', 0, 3)"), STR("//title | //link"), STR("x//title"), STR("x/title"), STR("id('a')//title"), STR("//*[@about]"), +		STR("count(descendant::*)"), STR("count(descendant::*) + count(ancestor::*)"), STR("@*|text()"), STR("*|/"), STR("source|destination"), +		STR("page != 'to' and page != 'from'"), STR("substring-after(icon/@image, '/mapfiles/marker')"), STR("substring-before(str, c)"), STR("page = 'from'"), +		STR("segments/@time"), STR("child::para"), STR("child::*"), STR("child::text()"), STR("child::node()"), STR("attribute::name"), STR("attribute::*"), +		STR("descendant::para"), STR("ancestor::div"), STR("ancestor-or-self::div"), STR("descendant-or-self::para"), STR("self::para"), STR("child::*/child::para"), +		STR("concat(substring-before(@image,'marker'),'icon',substring-after(@image,'marker'))"), STR("/"), STR("/descendant::para"), STR("/descendant::olist/child::item"), +		STR("child::para[position()=1]"), STR("child::para[position()=last()]"), STR("child::para[position()=last()-1]"), STR("child::para[position()>1]"), +		STR("following-sibling::chapter[position()=1]"), STR("preceding-sibling::chapter[position()=1]"), STR("/descendant::figure[position()=42]"), +		STR("/child::doc/child::chapter[position()=5]/child::section[position()=2]"), STR("child::chapter/descendant::para"), STR("child::para[attribute::type='warning']"), +		STR("child::para[attribute::type='warning'][position()=5]"), STR("child::para[position()=5][attribute::type='warning']"), STR("child::chapter[child::title='Introduction']"), +		STR("child::chapter[child::title]"), STR("child::*[self::chapter or self::appendix]"), STR("child::*[self::chapter or self::appendix][position()=last()]"), +		STR("count(//*[id='u1']|//*[id='u2'])"), STR("count(//*[id='u1']|//*[class='u'])"), STR("count(//*[class='u']|//*[class='u'])"), STR("count(//*[class='u']|//*[id='u1'])"), +		STR("count(//*[@id='self']/ancestor-or-self::*)"), STR("count(//*[@id='self']/ancestor::*)"), STR("count(//*[@id='self']/attribute::*)"), STR("count(//*[@id='self']/child::*)"), +		STR("count(//*[@id='self']/descendant-or-self::*)"), STR("count(//*[@id='self']/descendant::*)"), STR("count(//*[@id='self']/following-sibling::*)"), +		STR("count(//*[@id='self']/following::*)"), STR("//*[@id='self']/parent::*/@id"), STR("count(//*[@id='self']/preceding-sibling::*)"), +		STR("count(//*[@id='self']/preceding::*)"), STR("//*[@id='self']/self::*/@id"), STR("id('nested1')/div[1]//input[2]"), STR("id('foo')//div[contains(@id, 'useful')]//input"), +		STR("(//table[@class='stylee'])//th[text()='theHeaderText']/../td"), STR("address"), STR("address=string(/page/user/defaultlocation)"), STR("count-of-snippet-of-url = 0"), +		STR("daddr"), STR("form"), STR("form = 'from'"), STR("form = 'to'"), STR("form='near'"), STR("home"), STR("i"), STR("i > page and i < page + range"), +		STR("i < page and i >= page - range"), STR("i < @max"), STR("i <= page"), STR("i + 1"), STR("i = page"), STR("i = 1"), STR("info = position() or (not(info) and position() = 1)"), +		STR("is-first-order"), STR("is-first-order and snippets-exist"), STR("more"), STR("more > 0"), STR("near-point"), STR("page"), STR("page != 'from'"), STR("page != 'to'"), +		STR("page != 'to' and page != 'from'"), STR("page > 1"), STR("page = 'basics'"), STR("page = 'details'"), STR("page = 'from'"), STR("page = 'to'"), STR("page='from'"), +		STR("page='to'"), STR("r >= 0.5"), STR("r >= 1"), STR("r - 0"), STR("r - 1"), STR("r - 2"), STR("r - 3"), STR("r - 4"), STR("saddr"), STR("sources"), STR("sources[position() < details]"), +		STR("src"), STR("str"), STR("\"'\""), STR("(//location[string(info/references/reference[1]/url)=string(current-url)]/info/references/reference[1])[1]"), +		STR("(not(count-of-snippet-of-url = 0) and (position() = 1) or not(current-url = //locations/location[position() = last-pos]//reference[1]/url))"), +		STR("(not(info) and position() = 1) or info = position()"), STR("."), STR("../@arg0"), STR("../@filterpng"), STR("/page/@filterpng"), STR("4"), STR("@attribution"), +		STR("@id"), STR("@max > @num"), STR("@meters > 16093"), STR("@name"), STR("@start div @num + 1"), STR("@url"), STR("ad"), STR("address/line"), STR("adsmessage"), +		STR("attr"), STR("boolean(location[@id='near'][icon/@image])"), STR("bubble/node()"), STR("calltoaction/node()"), STR("category"), STR("contains(str, c)"), +		STR("count(//location[string(info/references/reference[1]/url)=string(current-url)]//snippet)"), STR("count(//snippet)"), STR("count(attr)"), STR("count(location)"), +		STR("count(structured/source) > 1"), STR("description/node()"), STR("destination"), STR("destinationAddress"), STR("domain"), STR("false()"), STR("icon/@class != 'noicon'"), +		STR("icon/@image"), STR("info"), STR("info/address/line"), STR("info/distance"), STR("info/distance and near-point"), STR("info/distance and info/phone and near-point"), +		STR("info/distance or info/phone"), STR("info/panel/node()"), STR("info/phone"), STR("info/references/reference[1]"), STR("info/references/reference[1]/snippet"), +		STR("info/references/reference[1]/url"), STR("info/title"), STR("info/title/node()"), STR("line"), STR("location"), STR("location[@id!='near']"), STR("location[@id='near'][icon/@image]"), +		STR("location[position() > umlocations div 2]"), STR("location[position() <= numlocations div 2]"), STR("locations"), STR("locations/location"), STR("near"), STR("node()"), +		STR("not(count-of-snippets = 0)"), STR("not(form = 'from')"), STR("not(form = 'near')"), STR("not(form = 'to')"), STR("not(../@page)"), STR("not(structured/source)"), STR("notice"), +		STR("number(../@info)"), STR("number(../@items)"), STR("number(/page/@linewidth)"), STR("page/ads"), STR("page/directions"), STR("page/error"), STR("page/overlay"), +		STR("page/overlay/locations/location"), STR("page/refinements"), STR("page/request/canonicalnear"), STR("page/request/near"), STR("page/request/query"), STR("page/spelling/suggestion"), +		STR("page/user/defaultlocation"), STR("phone"), STR("position()"), STR("position() != 1"), STR("position() != last()"), STR("position() > 1"), STR("position() < details"), +		STR("position()-1"), STR("query"), STR("references/@total"), STR("references/reference"), STR("references/reference/domain"), STR("references/reference/url"), +		STR("reviews/@positive div (reviews/@positive + reviews/@negative) * 5"), STR("reviews/@positive div (reviews/@positive + reviews/@negative) * (5)"), STR("reviews/@total"), +		STR("reviews/@total > 1"), STR("reviews/@total > 5"), STR("reviews/@total = 1"), STR("segments/@distance"), STR("segments/@time"), STR("segments/segment"), STR("shorttitle/node()"), +		STR("snippet"), STR("snippet/node()"), STR("source"), STR("sourceAddress"), STR("sourceAddress and destinationAddress"), STR("string(../@daddr)"), STR("string(../@form)"), +		STR("string(../@page)"), STR("string(../@saddr)"), STR("string(info/title)"), STR("string(page/request/canonicalnear) != ''"), STR("string(page/request/near) != ''"), +		STR("string-length(address) > linewidth"), STR("structured/@total - details"), STR("structured/source"), STR("structured/source[@name]"), STR("substring(address, 1, linewidth - 3)"), +		STR("substring-after(str, c)"), STR("substring-after(icon/@image, '/mapfiles/marker')"), STR("substring-before(str, c)"), STR("tagline/node()"), STR("targetedlocation"), +		STR("title"), STR("title/node()"), STR("true()"), STR("url"), STR("visibleurl"), STR("id(\"level10\")/ancestor::SPAN"), STR("id(\"level10\")/ancestor-or-self::SPAN"), STR("//attribute::*"), +        STR("child::HTML/child::BODY/child::H1"), STR("descendant::node()"), STR("descendant-or-self::SPAN"), STR("id(\"first\")/following::text()"), STR("id(\"first\")/following-sibling::node()"), +        STR("id(\"level10\")/parent::node()"), STR("id(\"last\")/preceding::text()"), STR("id(\"last\")/preceding-sibling::node()"), STR("/HTML/BODY/H1/self::node()"), STR("//*[@name]"), +        STR("id(\"pet\")/SELECT[@name=\"species\"]/OPTION[@selected]/@value"), STR("descendant::INPUT[@name=\"name\"]/@value"), STR("id(\"pet\")/INPUT[@name=\"gender\" and @checked]/@value"), +        STR("//TEXTAREA[@name=\"description\"]/text()"), STR("id(\"div1\")|id(\"div2\")|id(\"div3 div4 div5\")"), STR("//LI[1]"), STR("//LI[last()]/text()"), STR("//LI[position() mod 2]/@class"), +        STR("//text()[.=\"foo\"]"), STR("descendant-or-self::SPAN[position() > 2]"), STR("descendant::*[contains(@class,\" fruit \")]"), + +		// ajaxslt considers this path invalid, however I believe it's valid as per spec +		STR("***"), + +		// Oasis MSFT considers this path invalid, however I believe it's valid as per spec +		STR("**..**"), + +		// Miscellaneous +		STR("..***..***.***.***..***..***..") +    }; + +	for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) +	{ +		xpath_query q(paths[i]); +	} +} + +TEST(xpath_parse_paths_valid_unicode) +{ +    // From ajaxslt +	const wchar_t* paths[] = +	{ +	#ifdef U_LITERALS +		L"/descendant-or-self::\u90e8\u5206", L"//\u90e8\u5206", L"substring('\uff11\uff12\uff13\uff14\uff15', 0, 3)", L"//\u30bf\u30a4\u30c8\u30eb | //\u30ea\u30f3\u30af", +		L"\u8b0e//\u30bf\u30a4\u30c8\u30eb", L"//*[@\u30c7\u30b9\u30c6\u30a3\u30cd\u30a4\u30b7\u30e7\u30f3]", L"\u30da\u30fc\u30b8 = '\u304b\u3089'", +		L"concat(substring-before(@\u30a4\u30e1\u30fc\u30b8,'\u76ee\u5370'),'\u30a2\u30a4\u30b3\u30f3',substring-after(@\u30a4\u30e1\u30fc\u30b8,'\u76ee\u5370'))", +		L"\u30bd\u30fc\u30b9|\u30c7\u30b9\u30c6\u30a3\u30cd\u30a4\u30b7\u30e7\u30f3", L"\u30da\u30fc\u30b8 != '\u307e\u3067' and \u30da\u30fc\u30b8 != '\u304b\u3089'", +		L"substring-after(\u30a2\u30a4\u30b3\u30f3/@\u30a4\u30e1\u30fc\u30b8, '/\u5730\u56f3\u30d5\u30a1\u30a4\u30eb/\u76ee\u5370')", L"child::\u6bb5\u843d", +		L"substring-before(\u6587\u5b57\u5217, \u6587\u5b57)", L"\u30bb\u30b0\u30e1\u30f3\u30c8/@\u6642\u523b", L"attribute::\u540d\u524d", L"descendant::\u6bb5\u843d", +		L"ancestor::\u90e8\u5206", L"ancestor-or-self::\u90e8\u5206", L"descendant-or-self::\u6bb5\u843d", L"self::\u6bb5\u843d", L"child::\u7ae0/descendant::\u6bb5\u843d", +		L"child::*/child::\u6bb5\u843d", L"/descendant::\u6bb5\u843d", L"/descendant::\u9806\u5e8f\u30ea\u30b9\u30c8/child::\u9805\u76ee", L"child::\u6bb5\u843d[position()=1]", +		L"child::\u6bb5\u843d[position()=last()]", L"child::\u6bb5\u843d[position()=last()-1]", L"child::\u6bb5\u843d[position()>1]", L"following-sibling::\u7ae0[position()=1]", +		L"preceding-sibling::\u7ae0[position()=1]", L"/descendant::\u56f3\u8868[position()=42]", L"/child::\u6587\u66f8/child::\u7ae0[position()=5]/child::\u7bc0[position()=2]", +		L"child::\u6bb5\u843d[attribute::\u30bf\u30a4\u30d7='\u8b66\u544a']", L"child::\u6bb5\u843d[attribute::\u30bf\u30a4\u30d7='\u8b66\u544a'][position()=5]", +		L"child::\u6bb5\u843d[position()=5][attribute::\u30bf\u30a4\u30d7='\u8b66\u544a']", L"child::\u7ae0[child::\u30bf\u30a4\u30c8\u30eb='\u306f\u3058\u3081\u306b']", +		L"child::\u7ae0[child::\u30bf\u30a4\u30c8\u30eb]", L"child::*[self::\u7ae0 or self::\u4ed8\u9332]", L"child::*[self::\u7ae0 or self::\u4ed8\u9332][position()=last()]", +	#else +		L"/descendant-or-self::\x90e8\x5206", L"//\x90e8\x5206", L"substring('\xff11\xff12\xff13\xff14\xff15', 0, 3)", L"//\x30bf\x30a4\x30c8\x30eb | //\x30ea\x30f3\x30af", +		L"\x8b0e//\x30bf\x30a4\x30c8\x30eb", L"//*[@\x30c7\x30b9\x30c6\x30a3\x30cd\x30a4\x30b7\x30e7\x30f3]", L"\x30da\x30fc\x30b8 = '\x304b\x3089'", +		L"concat(substring-before(@\x30a4\x30e1\x30fc\x30b8,'\x76ee\x5370'),'\x30a2\x30a4\x30b3\x30f3',substring-after(@\x30a4\x30e1\x30fc\x30b8,'\x76ee\x5370'))", +		L"\x30bd\x30fc\x30b9|\x30c7\x30b9\x30c6\x30a3\x30cd\x30a4\x30b7\x30e7\x30f3", L"\x30da\x30fc\x30b8 != '\x307e\x3067' and \x30da\x30fc\x30b8 != '\x304b\x3089'", +		L"substring-after(\x30a2\x30a4\x30b3\x30f3/@\x30a4\x30e1\x30fc\x30b8, '/\x5730\x56f3\x30d5\x30a1\x30a4\x30eb/\x76ee\x5370')", L"child::\x6bb5\x843d", +		L"substring-before(\x6587\x5b57\x5217, \x6587\x5b57)", L"\x30bb\x30b0\x30e1\x30f3\x30c8/@\x6642\x523b", L"attribute::\x540d\x524d", L"descendant::\x6bb5\x843d", +		L"ancestor::\x90e8\x5206", L"ancestor-or-self::\x90e8\x5206", L"descendant-or-self::\x6bb5\x843d", L"self::\x6bb5\x843d", L"child::\x7ae0/descendant::\x6bb5\x843d", +		L"child::*/child::\x6bb5\x843d", L"/descendant::\x6bb5\x843d", L"/descendant::\x9806\x5e8f\x30ea\x30b9\x30c8/child::\x9805\x76ee", L"child::\x6bb5\x843d[position()=1]", +		L"child::\x6bb5\x843d[position()=last()]", L"child::\x6bb5\x843d[position()=last()-1]", L"child::\x6bb5\x843d[position()>1]", L"following-sibling::\x7ae0[position()=1]", +		L"preceding-sibling::\x7ae0[position()=1]", L"/descendant::\x56f3\x8868[position()=42]", L"/child::\x6587\x66f8/child::\x7ae0[position()=5]/child::\x7bc0[position()=2]", +		L"child::\x6bb5\x843d[attribute::\x30bf\x30a4\x30d7='\x8b66\x544a']", L"child::\x6bb5\x843d[attribute::\x30bf\x30a4\x30d7='\x8b66\x544a'][position()=5]", +		L"child::\x6bb5\x843d[position()=5][attribute::\x30bf\x30a4\x30d7='\x8b66\x544a']", L"child::\x7ae0[child::\x30bf\x30a4\x30c8\x30eb='\x306f\x3058\x3081\x306b']", +		L"child::\x7ae0[child::\x30bf\x30a4\x30c8\x30eb]", L"child::*[self::\x7ae0 or self::\x4ed8\x9332]", L"child::*[self::\x7ae0 or self::\x4ed8\x9332][position()=last()]", +	#endif +	}; + +	for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) +	{ +	#if defined(PUGIXML_WCHAR_MODE) +		xpath_query q(paths[i]); +	#elif !defined(PUGIXML_NO_STL) +		std::basic_string<char> path_utf8 = pugi::as_utf8(paths[i]); +		xpath_query q(path_utf8.c_str()); +	#endif +	} +} + +TEST(xpath_parse_invalid) +{ +    const char_t* paths[] = +	{ +		// From Jaxen tests +		STR("//:p"), STR("/foo/bar/"), STR("12 + (count(//author)+count(//author/attribute::*)) / 2"), STR("id()/2"), STR("+"), +		STR("///triple slash"), STR("/numbers numbers"), STR("/a/b[c > d]efg"), STR("/inv/child::"), STR("/invoice/@test[abcd"), +		STR("/invoice/@test[abcd > x"), STR("string-length('a"), STR("/descendant::()"), STR("(1 + 1"), STR("!false()"), +		STR("$author"), STR("10 + $foo"), STR("$foo:bar"), STR("$varname[@a='1']"), STR("foo/$variable/foo"), +		STR(".[1]"), STR("chyld::foo"), STR("foo/tacos()"), STR("foo/tacos()"), STR("/foo/bar[baz"), STR("//"), STR("*:foo"), +        STR("/cracker/cheese[(mold > 1) and (sense/taste"), + +		// From xpath-as3 tests +		STR("a b"), STR("//self::node())"), STR("/x/y[contains(self::node())"), STR("/x/y[contains(self::node()]"), STR("///"), STR("text::a"), + +		// From haXe-xpath tests +		STR("|/gjs"), STR("+3"), STR("/html/body/p != ---'div'/a"), STR(""), STR("@"), STR("#akf"), STR(",") + +		// Miscellaneous +		STR("..."), STR("...."), STR("**"), STR("****"), STR("******"), STR("..***..***.***.***..***..***..*"), STR("/[1]") +	}; + +	for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) +	{ +		CHECK_XPATH_FAIL(paths[i]); +	} +} + +TEST_XML(xpath_parse_absolute, "<div><s/></div>") +{ +	CHECK_XPATH_NODESET(doc, STR("/")) % 1; + +	CHECK_XPATH_NODESET(doc, STR("/div/s")) % 3; +	CHECK_XPATH_NODESET(doc, STR("/ div /s")) % 3; +	CHECK_XPATH_FAIL(STR("/ div 5")); + +	CHECK_XPATH_NODESET(doc, STR("/*/s")) % 3; +	CHECK_XPATH_NODESET(doc, STR("/ * /s")) % 3; +	CHECK_XPATH_FAIL(STR("/ * 5")); + +	CHECK_XPATH_NODESET(doc, STR("/*[/]")) % 2; +} + +#endif diff --git a/tests/test_xpath_paths.cpp b/tests/test_xpath_paths.cpp index b726a5a..2799f40 100644 --- a/tests/test_xpath_paths.cpp +++ b/tests/test_xpath_paths.cpp @@ -1,472 +1,472 @@ -#ifndef PUGIXML_NO_XPATH
 -
 -#include "common.hpp"
 -
 -TEST_XML(xpath_paths_axes_child, "<node attr='value'><child attr='value'><subchild/></child><another/><last/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("child:: node()"));
 -
 -	CHECK_XPATH_NODESET(n, STR("child:: node()")) % 4 % 7 % 8; // child, another, last
 -	CHECK_XPATH_NODESET(n, STR("another/child:: node()"));
 -
 -	CHECK_XPATH_NODESET(n, STR("@attr/child::node()"));
 -}
 -
 -TEST_XML(xpath_paths_axes_descendant, "<node attr='value'><child attr='value'><subchild/></child><another><subchild/></another><last/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("descendant:: node()"));
 -
 -	CHECK_XPATH_NODESET(n, STR("descendant:: node()")) % 4 % 6 % 7 % 8 % 9; // child, subchild, another, subchild, last
 -	CHECK_XPATH_NODESET(doc, STR("descendant:: node()")) % 2 % 4 % 6 % 7 % 8 % 9; // node, child, subchild, another, subchild, last
 -	CHECK_XPATH_NODESET(n, STR("another/descendant:: node()")) % 8; // subchild
 -	CHECK_XPATH_NODESET(n, STR("last/descendant:: node()"));
 -
 -	CHECK_XPATH_NODESET(n, STR("@attr/descendant::node()"));
 -}
 -
 -TEST_XML(xpath_paths_axes_parent, "<node attr='value'><child attr='value'><subchild/></child><another><subchild/></another><last/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("parent:: node()"));
 -
 -	CHECK_XPATH_NODESET(n.child(STR("child")), STR("parent:: node()")) % 2; // node
 -	CHECK_XPATH_NODESET(n, STR("child/subchild/parent:: node()")) % 4; // child
 -	CHECK_XPATH_NODESET(n, STR("@attr/parent:: node()")) % 2; // node
 -	CHECK_XPATH_NODESET(n, STR("parent:: node()")) % 1; // root
 -	CHECK_XPATH_NODESET(doc, STR("parent:: node()"));
 -}
 -
 -TEST_XML(xpath_paths_axes_ancestor, "<node attr='value'><child attr='value'><subchild/></child><another><subchild/></another><last/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("ancestor:: node()"));
 -
 -	CHECK_XPATH_NODESET(n.child(STR("child")), STR("ancestor:: node()")) % 2 % 1; // node, root
 -	CHECK_XPATH_NODESET(n, STR("child/subchild/ancestor:: node()")) % 4 % 2 % 1; // child, node, root
 -	CHECK_XPATH_NODESET(n, STR("child/@attr/ancestor:: node()")) % 4 % 2 % 1; // child, node, root
 -	CHECK_XPATH_NODESET(n, STR("ancestor:: node()")) % 1; // root
 -	CHECK_XPATH_NODESET(doc, STR("ancestor:: node()"));
 -}
 -
 -TEST_XML(xpath_paths_axes_following_sibling, "<node attr1='value' attr2='value'><child attr='value'><subchild/></child><another><subchild/></another><last/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("following-sibling:: node()"));
 -
 -	CHECK_XPATH_NODESET(n.child(STR("child")), STR("following-sibling:: node()")) % 8 % 10; // another, last
 -	CHECK_XPATH_NODESET(n.child(STR("last")), STR("following-sibling:: node()"));
 -	CHECK_XPATH_NODESET(n, STR("@attr1/following-sibling:: node()")); // attributes are not siblings
 -}
 -
 -TEST_XML(xpath_paths_axes_preceding_sibling, "<node attr1='value' attr2='value'><child attr='value'><subchild/></child><another><subchild/></another><last/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("preceding-sibling:: node()"));
 -
 -	CHECK_XPATH_NODESET(n.child(STR("child")), STR("preceding-sibling:: node()"));
 -	CHECK_XPATH_NODESET(n.child(STR("last")), STR("preceding-sibling:: node()")) % 8 % 5; // another, child
 -	CHECK_XPATH_NODESET(n, STR("@attr2/following-sibling:: node()")); // attributes are not siblings
 -}
 -
 -TEST_XML(xpath_paths_axes_following, "<node attr1='value' attr2='value'><child attr='value'><subchild/></child><another><subchild/></another><almost/><last/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("following:: node()"));
 -
 -	CHECK_XPATH_NODESET(n, STR("following:: node()")); // no descendants
 -	CHECK_XPATH_NODESET(n.child(STR("child")), STR("following:: node()")) % 8 % 9 % 10 % 11; // another, subchild, almost, last
 -	CHECK_XPATH_NODESET(n.child(STR("child")).child(STR("subchild")), STR("following:: node()")) % 8 % 9 % 10 % 11; // another, subchild, almost, last
 -	CHECK_XPATH_NODESET(n.child(STR("last")), STR("following:: node()"));
 -
 -	CHECK_XPATH_NODESET(n, STR("@attr1/following::node()")) % 5 % 7 % 8 % 9 % 10 % 11; // child, subchild, another, subchild, almost, last - because @/following
 -	CHECK_XPATH_NODESET(n, STR("child/@attr/following::node()")) % 7 % 8 % 9 % 10 % 11; // subchild, another, subchild, almost, last
 -}
 -
 -TEST_XML(xpath_paths_axes_preceding, "<node attr1='value' attr2='value'><child attr='value'><subchild/></child><another><subchild id='1'/></another><almost/><last/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("preceding:: node()"));
 -
 -	CHECK_XPATH_NODESET(n.child(STR("child")), STR("preceding:: node()")); // no ancestors
 -	CHECK_XPATH_NODESET(n.child(STR("last")), STR("preceding:: node()")) % 11 % 9 % 8 % 7 % 5; // almost, subchild, another, subchild, child
 -	CHECK_XPATH_NODESET(n.child(STR("another")).child(STR("subchild")), STR("preceding:: node()")) % 7 % 5; // subchild, child
 -	CHECK_XPATH_NODESET(n, STR("preceding:: node()"));
 -
 -	CHECK_XPATH_NODESET(n, STR("child/@attr/preceding::node()")); // no ancestors
 -	CHECK_XPATH_NODESET(n, STR("//subchild[@id]/@id/preceding::node()")) % 7 % 5; // subchild, child
 -}
 -
 -TEST_XML(xpath_paths_axes_attribute, "<node attr1='value' attr2='value'><child attr='value'><subchild/></child><another xmlns:foo='bar'><subchild/></another><last/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("attribute:: node()"));
 -
 -	CHECK_XPATH_NODESET(n.child(STR("child")), STR("attribute:: node()")) % 6; // child/@attr
 -	CHECK_XPATH_NODESET(n.child(STR("last")), STR("attribute:: node()"));
 -	CHECK_XPATH_NODESET(n, STR("attribute:: node()")) % 3 % 4; // node/@attr1 node/@attr2
 -	CHECK_XPATH_NODESET(doc, STR("descendant-or-self:: node()/attribute:: node()")) % 3 % 4 % 6; // all attributes
 -	CHECK_XPATH_NODESET(n.child(STR("another")), STR("attribute:: node()")); // namespace nodes are not attributes
 -
 -	CHECK_XPATH_NODESET(n, STR("@attr1/attribute::node()"));
 -}
 -
 -TEST_XML(xpath_paths_axes_namespace, "<node xmlns:foo='bar' attr='value'/>")
 -{
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(n, STR("namespace:: node()")); // namespace nodes are not supported
 -	CHECK_XPATH_NODESET(n, STR("@attr/attribute::node()"));
 -}
 -
 -TEST_XML(xpath_paths_axes_self, "<node attr='value'><child attr='value'><subchild/></child><another><subchild/></another><last/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("self:: node()"));
 -
 -	CHECK_XPATH_NODESET(n.child(STR("child")), STR("self:: node()")) % 4; // child
 -	CHECK_XPATH_NODESET(n, STR("self:: node()")) % 2; // node
 -	CHECK_XPATH_NODESET(n, STR("child/self:: node()")) % 4; // child
 -	CHECK_XPATH_NODESET(n, STR("child/@attr/self:: node()")) % 5; // @attr
 -	CHECK_XPATH_NODESET(doc, STR("self:: node()")) % 1; // root
 -}
 -
 -TEST_XML(xpath_paths_axes_descendant_or_self, "<node attr='value'><child attr='value'><subchild/></child><another><subchild/></another><last/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("descendant-or-self:: node()"));
 -
 -	CHECK_XPATH_NODESET(n, STR("descendant-or-self:: node()")) % 2 % 4 % 6 % 7 % 8 % 9; // node, child, subchild, another, subchild, last
 -	CHECK_XPATH_NODESET(doc, STR("descendant-or-self:: node()")) % 1 % 2 % 4 % 6 % 7 % 8 % 9; // root, node, child, subchild, another, subchild, last
 -	CHECK_XPATH_NODESET(n, STR("another/descendant-or-self:: node()")) % 7 % 8; // another, subchild
 -	CHECK_XPATH_NODESET(n, STR("last/descendant-or-self:: node()")) % 9; // last
 -
 -	CHECK_XPATH_NODESET(n, STR("child/@attr/descendant-or-self::node()")) % 5; // @attr
 -}
 -
 -TEST_XML(xpath_paths_axes_ancestor_or_self, "<node attr='value'><child attr='value'><subchild/></child><another><subchild/></another><last/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("ancestor-or-self:: node()"));
 -
 -	CHECK_XPATH_NODESET(n.child(STR("child")), STR("ancestor-or-self:: node()")) % 4 % 2 % 1; // child, node, root
 -	CHECK_XPATH_NODESET(n, STR("child/subchild/ancestor-or-self:: node()")) % 6 % 4 % 2 % 1; // subchild, child, node, root
 -	CHECK_XPATH_NODESET(n, STR("child/@attr/ancestor-or-self:: node()")) % 5 % 4 % 2 % 1; // @attr, child, node, root
 -	CHECK_XPATH_NODESET(n, STR("ancestor-or-self:: node()")) % 2 % 1; // root, node
 -	CHECK_XPATH_NODESET(doc, STR("ancestor-or-self:: node()")) % 1; // root
 -	CHECK_XPATH_NODESET(n, STR("ancestor-or-self:: node()")) % 2 % 1; // root, node
 -	CHECK_XPATH_NODESET(n, STR("last/ancestor-or-self::node()")) % 9 % 2 % 1; // root, node, last
 -}
 -
 -TEST_XML(xpath_paths_axes_abbrev, "<node attr='value'><foo/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	// @ axis
 -	CHECK_XPATH_NODESET(c, STR("@attr"));
 -	CHECK_XPATH_NODESET(n, STR("@attr")) % 3;
 -
 -	// no axis - child implied
 -	CHECK_XPATH_NODESET(c, STR("foo"));
 -	CHECK_XPATH_NODESET(n, STR("foo")) % 4;
 -	CHECK_XPATH_NODESET(doc, STR("node()")) % 2;
 -
 -	// @ axis should disable all other axis specifiers
 -	CHECK_XPATH_FAIL(STR("@child::foo"));
 -	CHECK_XPATH_FAIL(STR("@attribute::foo"));
 -}
 -
 -TEST_XML(xpath_paths_nodetest_all, "<node a1='v1' x:a2='v2'><c1/><x:c2/><c3/><x:c4/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("*"));
 -	CHECK_XPATH_NODESET(c, STR("child::*"));
 -
 -	CHECK_XPATH_NODESET(n, STR("*")) % 5 % 6 % 7 % 8;
 -	CHECK_XPATH_NODESET(n, STR("child::*")) % 5 % 6 % 7 % 8;
 -	CHECK_XPATH_NODESET(n, STR("attribute::*")) % 3 % 4;
 -}
 -
 -TEST_XML_FLAGS(xpath_paths_nodetest_name, "<node a1='v1' x:a2='v2'><c1/><x:c2/><c3/><x:c4/><?c1?></node>", parse_default | parse_pi)
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("c1"));
 -	CHECK_XPATH_NODESET(c, STR("child::c1"));
 -
 -	CHECK_XPATH_NODESET(n, STR("c1")) % 5;
 -	CHECK_XPATH_NODESET(n, STR("x:c2")) % 6;
 -
 -	CHECK_XPATH_NODESET(n, STR("child::c1")) % 5;
 -	CHECK_XPATH_NODESET(n, STR("child::x:c2")) % 6;
 -
 -	CHECK_XPATH_NODESET(n, STR("attribute::a1")) % 3;
 -	CHECK_XPATH_NODESET(n, STR("attribute::x:a2")) % 4;
 -	CHECK_XPATH_NODESET(n, STR("@x:a2")) % 4;
 -}
 -
 -TEST_XML(xpath_paths_nodetest_all_in_namespace, "<node a1='v1' x:a2='v2'><c1/><x:c2/><c3/><x:c4/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("x:*"));
 -	CHECK_XPATH_NODESET(c, STR("child::x:*"));
 -
 -	CHECK_XPATH_NODESET(n, STR("x:*")) % 6 % 8;
 -	CHECK_XPATH_NODESET(n, STR("child::x:*")) % 6 % 8;
 -
 -	CHECK_XPATH_NODESET(n, STR("attribute::x:*")) % 4;
 -	CHECK_XPATH_NODESET(n, STR("@x:*")) % 4;
 -
 -	CHECK_XPATH_FAIL(STR(":*"));
 -	CHECK_XPATH_FAIL(STR("@:*"));
 -}
 -
 -TEST_XML_FLAGS(xpath_paths_nodetest_type, "<node attr='value'>pcdata<child/><?pi1 value?><?pi2 value?><!--comment--><![CDATA[cdata]]></node>", parse_default | parse_pi | parse_comments)
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	// check on empty nodes
 -	CHECK_XPATH_NODESET(c, STR("node()"));
 -	CHECK_XPATH_NODESET(c, STR("text()"));
 -	CHECK_XPATH_NODESET(c, STR("comment()"));
 -	CHECK_XPATH_NODESET(c, STR("processing-instruction()"));
 -	CHECK_XPATH_NODESET(c, STR("processing-instruction('foobar')"));
 -
 -	// child axis
 -	CHECK_XPATH_NODESET(n, STR("node()")) % 4 % 5 % 6 % 7 % 8 % 9;
 -	CHECK_XPATH_NODESET(n, STR("text()")) % 4 % 9;
 -	CHECK_XPATH_NODESET(n, STR("comment()")) % 8;
 -	CHECK_XPATH_NODESET(n, STR("processing-instruction()")) % 6 % 7;
 -	CHECK_XPATH_NODESET(n, STR("processing-instruction('pi2')")) % 7;
 -
 -	// attribute axis
 -	CHECK_XPATH_NODESET(n, STR("@node()")) % 3;
 -	CHECK_XPATH_NODESET(n, STR("@text()"));
 -	CHECK_XPATH_NODESET(n, STR("@comment()"));
 -	CHECK_XPATH_NODESET(n, STR("@processing-instruction()"));
 -	CHECK_XPATH_NODESET(n, STR("@processing-instruction('pi2')"));
 -
 -	// incorrect 'argument' number
 -	CHECK_XPATH_FAIL(STR("node('')"));
 -	CHECK_XPATH_FAIL(STR("text('')"));
 -	CHECK_XPATH_FAIL(STR("comment('')"));
 -	CHECK_XPATH_FAIL(STR("processing-instruction(1)"));
 -	CHECK_XPATH_FAIL(STR("processing-instruction('', '')"));
 -	CHECK_XPATH_FAIL(STR("processing-instruction(concat('a', 'b'))"));
 -}
 -
 -TEST_XML_FLAGS(xpath_paths_nodetest_principal, "<node attr='value'>pcdata<child/><?pi1 value?><?pi2 value?><!--comment--><![CDATA[cdata]]></node><abra:cadabra abra:arba=''/>", parse_default | parse_pi | parse_comments)
 -{
 -	// node() test is true for any node type
 -	CHECK_XPATH_NODESET(doc, STR("//node()")) % 2 % 4 % 5 % 6 % 7 % 8 % 9 % 10;
 -	CHECK_XPATH_NODESET(doc, STR("//attribute::node()")) % 3 % 11;
 -	CHECK_XPATH_NODESET(doc, STR("//attribute::node()/ancestor-or-self::node()")) % 1 % 2 % 3 % 10 % 11;
 -
 -	// name test is true only for node with principal node type (depends on axis)
 -	CHECK_XPATH_NODESET(doc, STR("node/child::child")) % 5;
 -	CHECK_XPATH_NODESET(doc, STR("node/attribute::attr")) % 3;
 -	CHECK_XPATH_NODESET(doc, STR("node/child::pi1"));
 -	CHECK_XPATH_NODESET(doc, STR("node/child::attr"));
 -	CHECK_XPATH_NODESET(doc, STR("node/child::child/self::child")) % 5;
 -	CHECK_XPATH_NODESET(doc, STR("node/attribute::attr/self::attr")); // attribute is not of element type
 -	CHECK_XPATH_NODESET(doc, STR("node/child::child/ancestor-or-self::child")) % 5;
 -	CHECK_XPATH_NODESET(doc, STR("node/attribute::attr/ancestor-or-self::attr")); // attribute is not of element type
 -	CHECK_XPATH_NODESET(doc, STR("node/child::child/descendant-or-self::child")) % 5;
 -	CHECK_XPATH_NODESET(doc, STR("node/attribute::attr/descendant-or-self::attr")); // attribute is not of element type
 -
 -	// any name test is true only for node with principal node type (depends on axis)
 -	CHECK_XPATH_NODESET(doc, STR("node/child::*")) % 5;
 -	CHECK_XPATH_NODESET(doc, STR("node/attribute::*")) % 3;
 -	CHECK_XPATH_NODESET(doc, STR("node/child::*/self::*")) % 5;
 -	CHECK_XPATH_NODESET(doc, STR("node/attribute::*/self::*")); // attribute is not of element type
 -	CHECK_XPATH_NODESET(doc, STR("node/child::*/ancestor-or-self::*")) % 5 % 2;
 -	CHECK_XPATH_NODESET(doc, STR("node/attribute::*/ancestor-or-self::*")) % 2; // attribute is not of element type
 -	CHECK_XPATH_NODESET(doc, STR("node/child::*/descendant-or-self::*")) % 5;
 -	CHECK_XPATH_NODESET(doc, STR("node/attribute::*/descendant-or-self::*")); // attribute is not of element type
 -
 -	// namespace test is true only for node with principal node type (depends on axis)
 -	CHECK_XPATH_NODESET(doc, STR("child::abra:*")) % 10;
 -	CHECK_XPATH_NODESET(doc, STR("child::abra:*/attribute::abra:*")) % 11;
 -	CHECK_XPATH_NODESET(doc, STR("child::abra:*/self::abra:*")) % 10;
 -	CHECK_XPATH_NODESET(doc, STR("child::abra:*/attribute::abra:*/self::abra:*")); // attribute is not of element type
 -	CHECK_XPATH_NODESET(doc, STR("child::abra:*/ancestor-or-self::abra:*")) % 10;
 -	CHECK_XPATH_NODESET(doc, STR("child::abra:*/attribute::abra:*/ancestor-or-self::abra:*")) % 10; // attribute is not of element type
 -	CHECK_XPATH_NODESET(doc, STR("child::abra:*/descendant-or-self::abra:*")) % 10;
 -	CHECK_XPATH_NODESET(doc, STR("child::abra:*/attribute::abra:*/descendant-or-self::abra:*")); // attribute is not of element type
 -}
 -
 -TEST_XML(xpath_paths_absolute, "<node><foo><foo/><foo/></foo></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("/foo"));
 -	CHECK_XPATH_NODESET(n, STR("/foo"));
 -	CHECK_XPATH_NODESET(n, STR("/node/foo")) % 3;
 -	CHECK_XPATH_NODESET(n.child(STR("foo")), STR("/node/foo")) % 3;
 -
 -	CHECK_XPATH_NODESET(c, STR("/"));
 -	CHECK_XPATH_NODESET(n, STR("/")) % 1;
 -	CHECK_XPATH_NODESET(n.child(STR("foo")), STR("/")) % 1;
 -}
 -
 -TEST_XML(xpath_paths_step_abbrev, "<node><foo/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("."));
 -	CHECK_XPATH_NODESET(c, STR(".."));
 -
 -	CHECK_XPATH_NODESET(n, STR(".")) % 2;
 -	CHECK_XPATH_NODESET(n, STR("..")) % 1;
 -	CHECK_XPATH_NODESET(n, STR("../node")) % 2;
 -	CHECK_XPATH_NODESET(n.child(STR("foo")), STR("..")) % 2;
 -
 -	CHECK_XPATH_FAIL(STR(".node"));
 -	CHECK_XPATH_FAIL(STR("..node"));
 -}
 -
 -TEST_XML(xpath_paths_relative_abbrev, "<node><foo><foo/><foo/></foo></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("foo//bar"));
 -
 -	CHECK_XPATH_NODESET(n, STR("foo/foo")) % 4 % 5;
 -	CHECK_XPATH_NODESET(n, STR("foo//foo")) % 4 % 5;
 -	CHECK_XPATH_NODESET(n, STR(".//foo")) % 3 % 4 % 5;
 -}
 -
 -TEST_XML(xpath_paths_absolute_abbrev, "<node><foo><foo/><foo/></foo></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("//bar"));
 -
 -	CHECK_XPATH_NODESET(n, STR("//foo")) % 3 % 4 % 5;
 -	CHECK_XPATH_NODESET(n.child(STR("foo")), STR("//foo")) % 3 % 4 % 5;
 -	CHECK_XPATH_NODESET(doc, STR("//foo")) % 3 % 4 % 5;
 -}
 -
 -TEST_XML(xpath_paths_predicate_boolean, "<node><chapter/><chapter/><chapter/><chapter/><chapter/></node>")
 -{
 -	xml_node n = doc.child(STR("node")).child(STR("chapter")).next_sibling().next_sibling();
 -
 -	CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[position()=1]")) % 6;
 -	CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[position()=2]")) % 7;
 -	CHECK_XPATH_NODESET(n, STR("preceding-sibling::chapter[position()=1]")) % 4;
 -	CHECK_XPATH_NODESET(n, STR("preceding-sibling::chapter[position()=2]")) % 3;
 -}
 -
 -TEST_XML(xpath_paths_predicate_number, "<node><chapter/><chapter/><chapter/><chapter/><chapter/></node>")
 -{
 -	xml_node n = doc.child(STR("node")).child(STR("chapter")).next_sibling().next_sibling();
 -
 -	CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[1]")) % 6;
 -	CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[2]")) % 7;
 -	CHECK_XPATH_NODESET(n, STR("preceding-sibling::chapter[1]")) % 4;
 -	CHECK_XPATH_NODESET(n, STR("preceding-sibling::chapter[2]")) % 3;
 -}
 -
 -TEST_XML(xpath_paths_predicate_several, "<node><employee/><employee secretary=''/><employee assistant=''/><employee secretary='' assistant=''/><employee assistant='' secretary=''/></node>")
 -{
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(n, STR("employee")) % 3 % 4 % 6 % 8 % 11;
 -	CHECK_XPATH_NODESET(n, STR("employee[@secretary]")) % 4 % 8 % 11;
 -	CHECK_XPATH_NODESET(n, STR("employee[@assistant]")) % 6 % 8 % 11;
 -	CHECK_XPATH_NODESET(n, STR("employee[@secretary][@assistant]")) % 8 % 11;
 -	CHECK_XPATH_NODESET(n, STR("employee[@assistant][@secretary]")) % 8 % 11;
 -	CHECK_XPATH_NODESET(n, STR("employee[@secretary and @assistant]")) % 8 % 11;
 -}
 -
 -TEST_XML(xpath_paths_predicate_filter_boolean, "<node><chapter/><chapter/><chapter/><chapter/><chapter/></node>")
 -{
 -	xml_node n = doc.child(STR("node")).child(STR("chapter")).next_sibling().next_sibling();
 -
 -	CHECK_XPATH_NODESET(n, STR("(following-sibling::chapter)[position()=1]")) % 6;
 -	CHECK_XPATH_NODESET(n, STR("(following-sibling::chapter)[position()=2]")) % 7;
 -	CHECK_XPATH_NODESET(n, STR("(preceding-sibling::chapter)[position()=1]")) % 3;
 -	CHECK_XPATH_NODESET(n, STR("(preceding-sibling::chapter)[position()=2]")) % 4;
 -}
 -
 -TEST_XML(xpath_paths_predicate_filter_number, "<node><chapter/><chapter/><chapter/><chapter/><chapter/></node>")
 -{
 -	xml_node n = doc.child(STR("node")).child(STR("chapter")).next_sibling().next_sibling();
 -
 -	CHECK_XPATH_NODESET(n, STR("(following-sibling::chapter)[1]")) % 6;
 -	CHECK_XPATH_NODESET(n, STR("(following-sibling::chapter)[2]")) % 7;
 -	CHECK_XPATH_NODESET(n, STR("(preceding-sibling::chapter)[1]")) % 3;
 -	CHECK_XPATH_NODESET(n, STR("(preceding-sibling::chapter)[2]")) % 4;
 -}
 -
 -TEST_XML(xpath_paths_predicate_filter_posinv, "<node><employee/><employee secretary=''/><employee assistant=''/><employee secretary='' assistant=''/><employee assistant='' secretary=''/></node>")
 -{
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(n, STR("employee")) % 3 % 4 % 6 % 8 % 11;
 -	CHECK_XPATH_NODESET(n, STR("(employee[@secretary])[@assistant]")) % 8 % 11;
 -	CHECK_XPATH_NODESET(n, STR("((employee)[@assistant])[@secretary]")) % 8 % 11;
 -}
 -
 -TEST_XML(xpath_paths_step_compose, "<node><foo><foo/><foo/></foo><foo/></node>")
 -{
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(n, STR("(.)/foo")) % 3 % 6;
 -	CHECK_XPATH_NODESET(n, STR("(.)//foo")) % 3 % 4 % 5 % 6;
 -	CHECK_XPATH_NODESET(n, STR("(./..)//*")) % 2 % 3 % 4 % 5 % 6;
 -
 -	CHECK_XPATH_FAIL(STR("(1)/foo"));
 -	CHECK_XPATH_FAIL(STR("(1)//foo"));
 -}
 -
 -TEST_XML(xpath_paths_descendant_double_slash_w3c, "<node><para><para/><para/><para><para/></para></para><para/></node>")
 -{
 -	CHECK_XPATH_NODESET(doc, STR("//para")) % 3 % 4 % 5 % 6 % 7 % 8;
 -	CHECK_XPATH_NODESET(doc, STR("/descendant::para")) % 3 % 4 % 5 % 6 % 7 % 8;
 -	CHECK_XPATH_NODESET(doc, STR("//para[1]")) % 3 % 4 % 7;
 -	CHECK_XPATH_NODESET(doc, STR("/descendant::para[1]")) % 3;
 -}
 -
 -TEST_XML(xpath_paths_needs_sorting, "<node><child/><child/><child><subchild/><subchild/></child></node>")
 -{
 -    CHECK_XPATH_NODESET(doc, STR("(node/child/subchild)[2]")) % 7;
 -}
 -
 -#endif
 +#ifndef PUGIXML_NO_XPATH + +#include "common.hpp" + +TEST_XML(xpath_paths_axes_child, "<node attr='value'><child attr='value'><subchild/></child><another/><last/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("child:: node()")); + +	CHECK_XPATH_NODESET(n, STR("child:: node()")) % 4 % 7 % 8; // child, another, last +	CHECK_XPATH_NODESET(n, STR("another/child:: node()")); + +	CHECK_XPATH_NODESET(n, STR("@attr/child::node()")); +} + +TEST_XML(xpath_paths_axes_descendant, "<node attr='value'><child attr='value'><subchild/></child><another><subchild/></another><last/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("descendant:: node()")); + +	CHECK_XPATH_NODESET(n, STR("descendant:: node()")) % 4 % 6 % 7 % 8 % 9; // child, subchild, another, subchild, last +	CHECK_XPATH_NODESET(doc, STR("descendant:: node()")) % 2 % 4 % 6 % 7 % 8 % 9; // node, child, subchild, another, subchild, last +	CHECK_XPATH_NODESET(n, STR("another/descendant:: node()")) % 8; // subchild +	CHECK_XPATH_NODESET(n, STR("last/descendant:: node()")); + +	CHECK_XPATH_NODESET(n, STR("@attr/descendant::node()")); +} + +TEST_XML(xpath_paths_axes_parent, "<node attr='value'><child attr='value'><subchild/></child><another><subchild/></another><last/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("parent:: node()")); + +	CHECK_XPATH_NODESET(n.child(STR("child")), STR("parent:: node()")) % 2; // node +	CHECK_XPATH_NODESET(n, STR("child/subchild/parent:: node()")) % 4; // child +	CHECK_XPATH_NODESET(n, STR("@attr/parent:: node()")) % 2; // node +	CHECK_XPATH_NODESET(n, STR("parent:: node()")) % 1; // root +	CHECK_XPATH_NODESET(doc, STR("parent:: node()")); +} + +TEST_XML(xpath_paths_axes_ancestor, "<node attr='value'><child attr='value'><subchild/></child><another><subchild/></another><last/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("ancestor:: node()")); + +	CHECK_XPATH_NODESET(n.child(STR("child")), STR("ancestor:: node()")) % 2 % 1; // node, root +	CHECK_XPATH_NODESET(n, STR("child/subchild/ancestor:: node()")) % 4 % 2 % 1; // child, node, root +	CHECK_XPATH_NODESET(n, STR("child/@attr/ancestor:: node()")) % 4 % 2 % 1; // child, node, root +	CHECK_XPATH_NODESET(n, STR("ancestor:: node()")) % 1; // root +	CHECK_XPATH_NODESET(doc, STR("ancestor:: node()")); +} + +TEST_XML(xpath_paths_axes_following_sibling, "<node attr1='value' attr2='value'><child attr='value'><subchild/></child><another><subchild/></another><last/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("following-sibling:: node()")); + +	CHECK_XPATH_NODESET(n.child(STR("child")), STR("following-sibling:: node()")) % 8 % 10; // another, last +	CHECK_XPATH_NODESET(n.child(STR("last")), STR("following-sibling:: node()")); +	CHECK_XPATH_NODESET(n, STR("@attr1/following-sibling:: node()")); // attributes are not siblings +} + +TEST_XML(xpath_paths_axes_preceding_sibling, "<node attr1='value' attr2='value'><child attr='value'><subchild/></child><another><subchild/></another><last/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("preceding-sibling:: node()")); + +	CHECK_XPATH_NODESET(n.child(STR("child")), STR("preceding-sibling:: node()")); +	CHECK_XPATH_NODESET(n.child(STR("last")), STR("preceding-sibling:: node()")) % 8 % 5; // another, child +	CHECK_XPATH_NODESET(n, STR("@attr2/following-sibling:: node()")); // attributes are not siblings +} + +TEST_XML(xpath_paths_axes_following, "<node attr1='value' attr2='value'><child attr='value'><subchild/></child><another><subchild/></another><almost/><last/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("following:: node()")); + +	CHECK_XPATH_NODESET(n, STR("following:: node()")); // no descendants +	CHECK_XPATH_NODESET(n.child(STR("child")), STR("following:: node()")) % 8 % 9 % 10 % 11; // another, subchild, almost, last +	CHECK_XPATH_NODESET(n.child(STR("child")).child(STR("subchild")), STR("following:: node()")) % 8 % 9 % 10 % 11; // another, subchild, almost, last +	CHECK_XPATH_NODESET(n.child(STR("last")), STR("following:: node()")); + +	CHECK_XPATH_NODESET(n, STR("@attr1/following::node()")) % 5 % 7 % 8 % 9 % 10 % 11; // child, subchild, another, subchild, almost, last - because @/following +	CHECK_XPATH_NODESET(n, STR("child/@attr/following::node()")) % 7 % 8 % 9 % 10 % 11; // subchild, another, subchild, almost, last +} + +TEST_XML(xpath_paths_axes_preceding, "<node attr1='value' attr2='value'><child attr='value'><subchild/></child><another><subchild id='1'/></another><almost/><last/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("preceding:: node()")); + +	CHECK_XPATH_NODESET(n.child(STR("child")), STR("preceding:: node()")); // no ancestors +	CHECK_XPATH_NODESET(n.child(STR("last")), STR("preceding:: node()")) % 11 % 9 % 8 % 7 % 5; // almost, subchild, another, subchild, child +	CHECK_XPATH_NODESET(n.child(STR("another")).child(STR("subchild")), STR("preceding:: node()")) % 7 % 5; // subchild, child +	CHECK_XPATH_NODESET(n, STR("preceding:: node()")); + +	CHECK_XPATH_NODESET(n, STR("child/@attr/preceding::node()")); // no ancestors +	CHECK_XPATH_NODESET(n, STR("//subchild[@id]/@id/preceding::node()")) % 7 % 5; // subchild, child +} + +TEST_XML(xpath_paths_axes_attribute, "<node attr1='value' attr2='value'><child attr='value'><subchild/></child><another xmlns:foo='bar'><subchild/></another><last/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("attribute:: node()")); + +	CHECK_XPATH_NODESET(n.child(STR("child")), STR("attribute:: node()")) % 6; // child/@attr +	CHECK_XPATH_NODESET(n.child(STR("last")), STR("attribute:: node()")); +	CHECK_XPATH_NODESET(n, STR("attribute:: node()")) % 3 % 4; // node/@attr1 node/@attr2 +	CHECK_XPATH_NODESET(doc, STR("descendant-or-self:: node()/attribute:: node()")) % 3 % 4 % 6; // all attributes +	CHECK_XPATH_NODESET(n.child(STR("another")), STR("attribute:: node()")); // namespace nodes are not attributes + +	CHECK_XPATH_NODESET(n, STR("@attr1/attribute::node()")); +} + +TEST_XML(xpath_paths_axes_namespace, "<node xmlns:foo='bar' attr='value'/>") +{ +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(n, STR("namespace:: node()")); // namespace nodes are not supported +	CHECK_XPATH_NODESET(n, STR("@attr/attribute::node()")); +} + +TEST_XML(xpath_paths_axes_self, "<node attr='value'><child attr='value'><subchild/></child><another><subchild/></another><last/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("self:: node()")); + +	CHECK_XPATH_NODESET(n.child(STR("child")), STR("self:: node()")) % 4; // child +	CHECK_XPATH_NODESET(n, STR("self:: node()")) % 2; // node +	CHECK_XPATH_NODESET(n, STR("child/self:: node()")) % 4; // child +	CHECK_XPATH_NODESET(n, STR("child/@attr/self:: node()")) % 5; // @attr +	CHECK_XPATH_NODESET(doc, STR("self:: node()")) % 1; // root +} + +TEST_XML(xpath_paths_axes_descendant_or_self, "<node attr='value'><child attr='value'><subchild/></child><another><subchild/></another><last/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("descendant-or-self:: node()")); + +	CHECK_XPATH_NODESET(n, STR("descendant-or-self:: node()")) % 2 % 4 % 6 % 7 % 8 % 9; // node, child, subchild, another, subchild, last +	CHECK_XPATH_NODESET(doc, STR("descendant-or-self:: node()")) % 1 % 2 % 4 % 6 % 7 % 8 % 9; // root, node, child, subchild, another, subchild, last +	CHECK_XPATH_NODESET(n, STR("another/descendant-or-self:: node()")) % 7 % 8; // another, subchild +	CHECK_XPATH_NODESET(n, STR("last/descendant-or-self:: node()")) % 9; // last + +	CHECK_XPATH_NODESET(n, STR("child/@attr/descendant-or-self::node()")) % 5; // @attr +} + +TEST_XML(xpath_paths_axes_ancestor_or_self, "<node attr='value'><child attr='value'><subchild/></child><another><subchild/></another><last/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("ancestor-or-self:: node()")); + +	CHECK_XPATH_NODESET(n.child(STR("child")), STR("ancestor-or-self:: node()")) % 4 % 2 % 1; // child, node, root +	CHECK_XPATH_NODESET(n, STR("child/subchild/ancestor-or-self:: node()")) % 6 % 4 % 2 % 1; // subchild, child, node, root +	CHECK_XPATH_NODESET(n, STR("child/@attr/ancestor-or-self:: node()")) % 5 % 4 % 2 % 1; // @attr, child, node, root +	CHECK_XPATH_NODESET(n, STR("ancestor-or-self:: node()")) % 2 % 1; // root, node +	CHECK_XPATH_NODESET(doc, STR("ancestor-or-self:: node()")) % 1; // root +	CHECK_XPATH_NODESET(n, STR("ancestor-or-self:: node()")) % 2 % 1; // root, node +	CHECK_XPATH_NODESET(n, STR("last/ancestor-or-self::node()")) % 9 % 2 % 1; // root, node, last +} + +TEST_XML(xpath_paths_axes_abbrev, "<node attr='value'><foo/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	// @ axis +	CHECK_XPATH_NODESET(c, STR("@attr")); +	CHECK_XPATH_NODESET(n, STR("@attr")) % 3; + +	// no axis - child implied +	CHECK_XPATH_NODESET(c, STR("foo")); +	CHECK_XPATH_NODESET(n, STR("foo")) % 4; +	CHECK_XPATH_NODESET(doc, STR("node()")) % 2; + +	// @ axis should disable all other axis specifiers +	CHECK_XPATH_FAIL(STR("@child::foo")); +	CHECK_XPATH_FAIL(STR("@attribute::foo")); +} + +TEST_XML(xpath_paths_nodetest_all, "<node a1='v1' x:a2='v2'><c1/><x:c2/><c3/><x:c4/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("*")); +	CHECK_XPATH_NODESET(c, STR("child::*")); + +	CHECK_XPATH_NODESET(n, STR("*")) % 5 % 6 % 7 % 8; +	CHECK_XPATH_NODESET(n, STR("child::*")) % 5 % 6 % 7 % 8; +	CHECK_XPATH_NODESET(n, STR("attribute::*")) % 3 % 4; +} + +TEST_XML_FLAGS(xpath_paths_nodetest_name, "<node a1='v1' x:a2='v2'><c1/><x:c2/><c3/><x:c4/><?c1?></node>", parse_default | parse_pi) +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("c1")); +	CHECK_XPATH_NODESET(c, STR("child::c1")); + +	CHECK_XPATH_NODESET(n, STR("c1")) % 5; +	CHECK_XPATH_NODESET(n, STR("x:c2")) % 6; + +	CHECK_XPATH_NODESET(n, STR("child::c1")) % 5; +	CHECK_XPATH_NODESET(n, STR("child::x:c2")) % 6; + +	CHECK_XPATH_NODESET(n, STR("attribute::a1")) % 3; +	CHECK_XPATH_NODESET(n, STR("attribute::x:a2")) % 4; +	CHECK_XPATH_NODESET(n, STR("@x:a2")) % 4; +} + +TEST_XML(xpath_paths_nodetest_all_in_namespace, "<node a1='v1' x:a2='v2'><c1/><x:c2/><c3/><x:c4/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("x:*")); +	CHECK_XPATH_NODESET(c, STR("child::x:*")); + +	CHECK_XPATH_NODESET(n, STR("x:*")) % 6 % 8; +	CHECK_XPATH_NODESET(n, STR("child::x:*")) % 6 % 8; + +	CHECK_XPATH_NODESET(n, STR("attribute::x:*")) % 4; +	CHECK_XPATH_NODESET(n, STR("@x:*")) % 4; + +	CHECK_XPATH_FAIL(STR(":*")); +	CHECK_XPATH_FAIL(STR("@:*")); +} + +TEST_XML_FLAGS(xpath_paths_nodetest_type, "<node attr='value'>pcdata<child/><?pi1 value?><?pi2 value?><!--comment--><![CDATA[cdata]]></node>", parse_default | parse_pi | parse_comments) +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	// check on empty nodes +	CHECK_XPATH_NODESET(c, STR("node()")); +	CHECK_XPATH_NODESET(c, STR("text()")); +	CHECK_XPATH_NODESET(c, STR("comment()")); +	CHECK_XPATH_NODESET(c, STR("processing-instruction()")); +	CHECK_XPATH_NODESET(c, STR("processing-instruction('foobar')")); + +	// child axis +	CHECK_XPATH_NODESET(n, STR("node()")) % 4 % 5 % 6 % 7 % 8 % 9; +	CHECK_XPATH_NODESET(n, STR("text()")) % 4 % 9; +	CHECK_XPATH_NODESET(n, STR("comment()")) % 8; +	CHECK_XPATH_NODESET(n, STR("processing-instruction()")) % 6 % 7; +	CHECK_XPATH_NODESET(n, STR("processing-instruction('pi2')")) % 7; + +	// attribute axis +	CHECK_XPATH_NODESET(n, STR("@node()")) % 3; +	CHECK_XPATH_NODESET(n, STR("@text()")); +	CHECK_XPATH_NODESET(n, STR("@comment()")); +	CHECK_XPATH_NODESET(n, STR("@processing-instruction()")); +	CHECK_XPATH_NODESET(n, STR("@processing-instruction('pi2')")); + +	// incorrect 'argument' number +	CHECK_XPATH_FAIL(STR("node('')")); +	CHECK_XPATH_FAIL(STR("text('')")); +	CHECK_XPATH_FAIL(STR("comment('')")); +	CHECK_XPATH_FAIL(STR("processing-instruction(1)")); +	CHECK_XPATH_FAIL(STR("processing-instruction('', '')")); +	CHECK_XPATH_FAIL(STR("processing-instruction(concat('a', 'b'))")); +} + +TEST_XML_FLAGS(xpath_paths_nodetest_principal, "<node attr='value'>pcdata<child/><?pi1 value?><?pi2 value?><!--comment--><![CDATA[cdata]]></node><abra:cadabra abra:arba=''/>", parse_default | parse_pi | parse_comments) +{ +	// node() test is true for any node type +	CHECK_XPATH_NODESET(doc, STR("//node()")) % 2 % 4 % 5 % 6 % 7 % 8 % 9 % 10; +	CHECK_XPATH_NODESET(doc, STR("//attribute::node()")) % 3 % 11; +	CHECK_XPATH_NODESET(doc, STR("//attribute::node()/ancestor-or-self::node()")) % 1 % 2 % 3 % 10 % 11; + +	// name test is true only for node with principal node type (depends on axis) +	CHECK_XPATH_NODESET(doc, STR("node/child::child")) % 5; +	CHECK_XPATH_NODESET(doc, STR("node/attribute::attr")) % 3; +	CHECK_XPATH_NODESET(doc, STR("node/child::pi1")); +	CHECK_XPATH_NODESET(doc, STR("node/child::attr")); +	CHECK_XPATH_NODESET(doc, STR("node/child::child/self::child")) % 5; +	CHECK_XPATH_NODESET(doc, STR("node/attribute::attr/self::attr")); // attribute is not of element type +	CHECK_XPATH_NODESET(doc, STR("node/child::child/ancestor-or-self::child")) % 5; +	CHECK_XPATH_NODESET(doc, STR("node/attribute::attr/ancestor-or-self::attr")); // attribute is not of element type +	CHECK_XPATH_NODESET(doc, STR("node/child::child/descendant-or-self::child")) % 5; +	CHECK_XPATH_NODESET(doc, STR("node/attribute::attr/descendant-or-self::attr")); // attribute is not of element type + +	// any name test is true only for node with principal node type (depends on axis) +	CHECK_XPATH_NODESET(doc, STR("node/child::*")) % 5; +	CHECK_XPATH_NODESET(doc, STR("node/attribute::*")) % 3; +	CHECK_XPATH_NODESET(doc, STR("node/child::*/self::*")) % 5; +	CHECK_XPATH_NODESET(doc, STR("node/attribute::*/self::*")); // attribute is not of element type +	CHECK_XPATH_NODESET(doc, STR("node/child::*/ancestor-or-self::*")) % 5 % 2; +	CHECK_XPATH_NODESET(doc, STR("node/attribute::*/ancestor-or-self::*")) % 2; // attribute is not of element type +	CHECK_XPATH_NODESET(doc, STR("node/child::*/descendant-or-self::*")) % 5; +	CHECK_XPATH_NODESET(doc, STR("node/attribute::*/descendant-or-self::*")); // attribute is not of element type + +	// namespace test is true only for node with principal node type (depends on axis) +	CHECK_XPATH_NODESET(doc, STR("child::abra:*")) % 10; +	CHECK_XPATH_NODESET(doc, STR("child::abra:*/attribute::abra:*")) % 11; +	CHECK_XPATH_NODESET(doc, STR("child::abra:*/self::abra:*")) % 10; +	CHECK_XPATH_NODESET(doc, STR("child::abra:*/attribute::abra:*/self::abra:*")); // attribute is not of element type +	CHECK_XPATH_NODESET(doc, STR("child::abra:*/ancestor-or-self::abra:*")) % 10; +	CHECK_XPATH_NODESET(doc, STR("child::abra:*/attribute::abra:*/ancestor-or-self::abra:*")) % 10; // attribute is not of element type +	CHECK_XPATH_NODESET(doc, STR("child::abra:*/descendant-or-self::abra:*")) % 10; +	CHECK_XPATH_NODESET(doc, STR("child::abra:*/attribute::abra:*/descendant-or-self::abra:*")); // attribute is not of element type +} + +TEST_XML(xpath_paths_absolute, "<node><foo><foo/><foo/></foo></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("/foo")); +	CHECK_XPATH_NODESET(n, STR("/foo")); +	CHECK_XPATH_NODESET(n, STR("/node/foo")) % 3; +	CHECK_XPATH_NODESET(n.child(STR("foo")), STR("/node/foo")) % 3; + +	CHECK_XPATH_NODESET(c, STR("/")); +	CHECK_XPATH_NODESET(n, STR("/")) % 1; +	CHECK_XPATH_NODESET(n.child(STR("foo")), STR("/")) % 1; +} + +TEST_XML(xpath_paths_step_abbrev, "<node><foo/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR(".")); +	CHECK_XPATH_NODESET(c, STR("..")); + +	CHECK_XPATH_NODESET(n, STR(".")) % 2; +	CHECK_XPATH_NODESET(n, STR("..")) % 1; +	CHECK_XPATH_NODESET(n, STR("../node")) % 2; +	CHECK_XPATH_NODESET(n.child(STR("foo")), STR("..")) % 2; + +	CHECK_XPATH_FAIL(STR(".node")); +	CHECK_XPATH_FAIL(STR("..node")); +} + +TEST_XML(xpath_paths_relative_abbrev, "<node><foo><foo/><foo/></foo></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("foo//bar")); + +	CHECK_XPATH_NODESET(n, STR("foo/foo")) % 4 % 5; +	CHECK_XPATH_NODESET(n, STR("foo//foo")) % 4 % 5; +	CHECK_XPATH_NODESET(n, STR(".//foo")) % 3 % 4 % 5; +} + +TEST_XML(xpath_paths_absolute_abbrev, "<node><foo><foo/><foo/></foo></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("//bar")); + +	CHECK_XPATH_NODESET(n, STR("//foo")) % 3 % 4 % 5; +	CHECK_XPATH_NODESET(n.child(STR("foo")), STR("//foo")) % 3 % 4 % 5; +	CHECK_XPATH_NODESET(doc, STR("//foo")) % 3 % 4 % 5; +} + +TEST_XML(xpath_paths_predicate_boolean, "<node><chapter/><chapter/><chapter/><chapter/><chapter/></node>") +{ +	xml_node n = doc.child(STR("node")).child(STR("chapter")).next_sibling().next_sibling(); + +	CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[position()=1]")) % 6; +	CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[position()=2]")) % 7; +	CHECK_XPATH_NODESET(n, STR("preceding-sibling::chapter[position()=1]")) % 4; +	CHECK_XPATH_NODESET(n, STR("preceding-sibling::chapter[position()=2]")) % 3; +} + +TEST_XML(xpath_paths_predicate_number, "<node><chapter/><chapter/><chapter/><chapter/><chapter/></node>") +{ +	xml_node n = doc.child(STR("node")).child(STR("chapter")).next_sibling().next_sibling(); + +	CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[1]")) % 6; +	CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[2]")) % 7; +	CHECK_XPATH_NODESET(n, STR("preceding-sibling::chapter[1]")) % 4; +	CHECK_XPATH_NODESET(n, STR("preceding-sibling::chapter[2]")) % 3; +} + +TEST_XML(xpath_paths_predicate_several, "<node><employee/><employee secretary=''/><employee assistant=''/><employee secretary='' assistant=''/><employee assistant='' secretary=''/></node>") +{ +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(n, STR("employee")) % 3 % 4 % 6 % 8 % 11; +	CHECK_XPATH_NODESET(n, STR("employee[@secretary]")) % 4 % 8 % 11; +	CHECK_XPATH_NODESET(n, STR("employee[@assistant]")) % 6 % 8 % 11; +	CHECK_XPATH_NODESET(n, STR("employee[@secretary][@assistant]")) % 8 % 11; +	CHECK_XPATH_NODESET(n, STR("employee[@assistant][@secretary]")) % 8 % 11; +	CHECK_XPATH_NODESET(n, STR("employee[@secretary and @assistant]")) % 8 % 11; +} + +TEST_XML(xpath_paths_predicate_filter_boolean, "<node><chapter/><chapter/><chapter/><chapter/><chapter/></node>") +{ +	xml_node n = doc.child(STR("node")).child(STR("chapter")).next_sibling().next_sibling(); + +	CHECK_XPATH_NODESET(n, STR("(following-sibling::chapter)[position()=1]")) % 6; +	CHECK_XPATH_NODESET(n, STR("(following-sibling::chapter)[position()=2]")) % 7; +	CHECK_XPATH_NODESET(n, STR("(preceding-sibling::chapter)[position()=1]")) % 3; +	CHECK_XPATH_NODESET(n, STR("(preceding-sibling::chapter)[position()=2]")) % 4; +} + +TEST_XML(xpath_paths_predicate_filter_number, "<node><chapter/><chapter/><chapter/><chapter/><chapter/></node>") +{ +	xml_node n = doc.child(STR("node")).child(STR("chapter")).next_sibling().next_sibling(); + +	CHECK_XPATH_NODESET(n, STR("(following-sibling::chapter)[1]")) % 6; +	CHECK_XPATH_NODESET(n, STR("(following-sibling::chapter)[2]")) % 7; +	CHECK_XPATH_NODESET(n, STR("(preceding-sibling::chapter)[1]")) % 3; +	CHECK_XPATH_NODESET(n, STR("(preceding-sibling::chapter)[2]")) % 4; +} + +TEST_XML(xpath_paths_predicate_filter_posinv, "<node><employee/><employee secretary=''/><employee assistant=''/><employee secretary='' assistant=''/><employee assistant='' secretary=''/></node>") +{ +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(n, STR("employee")) % 3 % 4 % 6 % 8 % 11; +	CHECK_XPATH_NODESET(n, STR("(employee[@secretary])[@assistant]")) % 8 % 11; +	CHECK_XPATH_NODESET(n, STR("((employee)[@assistant])[@secretary]")) % 8 % 11; +} + +TEST_XML(xpath_paths_step_compose, "<node><foo><foo/><foo/></foo><foo/></node>") +{ +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(n, STR("(.)/foo")) % 3 % 6; +	CHECK_XPATH_NODESET(n, STR("(.)//foo")) % 3 % 4 % 5 % 6; +	CHECK_XPATH_NODESET(n, STR("(./..)//*")) % 2 % 3 % 4 % 5 % 6; + +	CHECK_XPATH_FAIL(STR("(1)/foo")); +	CHECK_XPATH_FAIL(STR("(1)//foo")); +} + +TEST_XML(xpath_paths_descendant_double_slash_w3c, "<node><para><para/><para/><para><para/></para></para><para/></node>") +{ +	CHECK_XPATH_NODESET(doc, STR("//para")) % 3 % 4 % 5 % 6 % 7 % 8; +	CHECK_XPATH_NODESET(doc, STR("/descendant::para")) % 3 % 4 % 5 % 6 % 7 % 8; +	CHECK_XPATH_NODESET(doc, STR("//para[1]")) % 3 % 4 % 7; +	CHECK_XPATH_NODESET(doc, STR("/descendant::para[1]")) % 3; +} + +TEST_XML(xpath_paths_needs_sorting, "<node><child/><child/><child><subchild/><subchild/></child></node>") +{ +    CHECK_XPATH_NODESET(doc, STR("(node/child/subchild)[2]")) % 7; +} + +#endif diff --git a/tests/test_xpath_paths_abbrev_w3c.cpp b/tests/test_xpath_paths_abbrev_w3c.cpp index ebd13aa..af65752 100644 --- a/tests/test_xpath_paths_abbrev_w3c.cpp +++ b/tests/test_xpath_paths_abbrev_w3c.cpp @@ -1,217 +1,217 @@ -#ifndef PUGIXML_NO_XPATH
 -
 -#include "common.hpp"
 -
 -TEST_XML(xpath_paths_abbrev_w3c_1, "<node><para/><foo/><para/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("para"));
 -	CHECK_XPATH_NODESET(n, STR("para")) % 3 % 5;
 -}
 -
 -TEST_XML(xpath_paths_abbrev_w3c_2, "<node><para/><foo/><para/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("*"));
 -	CHECK_XPATH_NODESET(n, STR("*")) % 3 % 4 % 5;
 -}
 -
 -TEST_XML(xpath_paths_abbrev_w3c_3, "<node>pcdata<child/><![CDATA[cdata]]></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("text()"));
 -	CHECK_XPATH_NODESET(n, STR("text()")) % 3 % 5;
 -}
 -
 -TEST_XML(xpath_paths_abbrev_w3c_4, "<node name='value' foo='bar' />")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("@name"));
 -	CHECK_XPATH_NODESET(n, STR("@name")) % 3;
 -}
 -
 -TEST_XML(xpath_paths_abbrev_w3c_5, "<node name='value' foo='bar' />")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("@*"));
 -	CHECK_XPATH_NODESET(n, STR("@*")) % 3 % 4;
 -}
 -
 -TEST_XML(xpath_paths_abbrev_w3c_6, "<node><para/><para/><para/><para/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("para[1]"));
 -	CHECK_XPATH_NODESET(n, STR("para[1]")) % 3;
 -}
 -
 -TEST_XML(xpath_paths_abbrev_w3c_7, "<node><para/><para/><para/><para/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("para[last()]"));
 -	CHECK_XPATH_NODESET(n, STR("para[last()]")) % 6;
 -}
 -
 -TEST_XML(xpath_paths_abbrev_w3c_8, "<node><para><para/><para/><foo><para/></foo></para><foo/><para/></node>")
 -{
 -	xml_node c;
 -
 -	CHECK_XPATH_NODESET(c, STR("*/para"));
 -	CHECK_XPATH_NODESET(doc, STR("*/para")) % 3 % 9;
 -}
 -
 -TEST_XML(xpath_paths_abbrev_w3c_9, "<doc><chapter/><chapter/><chapter/><chapter/><chapter><section/><section/><section/></chapter><chapter/></doc>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("doc")).child(STR("chapter"));
 -
 -	CHECK_XPATH_NODESET(c, STR("/doc/chapter[5]/section[2]"));
 -	CHECK_XPATH_NODESET(n, STR("/doc/chapter[5]/section[2]")) % 9;
 -	CHECK_XPATH_NODESET(doc, STR("/doc/chapter[5]/section[2]")) % 9;
 -}
 -
 -TEST_XML(xpath_paths_abbrev_w3c_10, "<chapter><para><para/><para/><foo><para/></foo></para><foo/><para/></chapter>")
 -{
 -	xml_node c;
 -
 -	CHECK_XPATH_NODESET(c, STR("chapter//para"));
 -	CHECK_XPATH_NODESET(doc, STR("chapter//para")) % 3 % 4 % 5 % 7 % 9;
 -}
 -
 -TEST_XML(xpath_paths_abbrev_w3c_11, "<node><para><para/><para/><foo><para/></foo></para><foo/><para/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("//para"));
 -	CHECK_XPATH_NODESET(n, STR("//para")) % 3 % 4 % 5 % 7 % 9;
 -	CHECK_XPATH_NODESET(n.child(STR("para")), STR("//para")) % 3 % 4 % 5 % 7 % 9;
 -}
 -
 -TEST_XML(xpath_paths_abbrev_w3c_12, "<node><olist><item/></olist><item/><olist><olist><item/><item/></olist></olist></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("//olist/item"));
 -	CHECK_XPATH_NODESET(n, STR("//olist/item")) % 4 % 8 % 9;
 -	CHECK_XPATH_NODESET(n.child(STR("olist")), STR("//olist/item")) % 4 % 8 % 9;
 -}
 -
 -TEST_XML(xpath_paths_abbrev_w3c_13, "<node><child/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("."));
 -	CHECK_XPATH_NODESET(n, STR(".")) % 2;
 -	CHECK_XPATH_NODESET(n.child(STR("child")), STR(".")) % 3;
 -}
 -
 -TEST_XML(xpath_paths_abbrev_w3c_14, "<node><para><para/><para/><foo><para/></foo></para><foo/><para/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR(".//para"));
 -	CHECK_XPATH_NODESET(n, STR(".//para")) % 3 % 4 % 5 % 7 % 9;
 -	CHECK_XPATH_NODESET(n.child(STR("para")), STR(".//para")) % 4 % 5 % 7;
 -}
 -
 -TEST_XML(xpath_paths_abbrev_w3c_15, "<node lang='en'><child/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR(".."));
 -	CHECK_XPATH_NODESET(n, STR("..")) % 1;
 -	CHECK_XPATH_NODESET(n.child(STR("child")), STR("..")) % 2;
 -}
 -
 -TEST_XML(xpath_paths_abbrev_w3c_16, "<node lang='en'><child/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("../@lang"));
 -	CHECK_XPATH_NODESET(n, STR("../@lang"));
 -	CHECK_XPATH_NODESET(n.child(STR("child")), STR("../@lang")) % 3;
 -}
 -
 -TEST_XML(xpath_paths_abbrev_w3c_17, "<node><para/><para type='warning'/><para type='warning'/><para/><para type='error'/><para type='warning'/><para type='warning'/><para type='warning'/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("para[@type=\"warning\"]"));
 -	CHECK_XPATH_NODESET(n, STR("para[@type=\"warning\"]")) % 4 % 6 % 11 % 13 % 15;
 -}
 -
 -TEST_XML(xpath_paths_abbrev_w3c_18, "<node><para/><para type='warning'/><para type='warning'/><para/><para type='error'/><para type='warning'/><para type='warning'/><para type='warning'/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("para[@type=\"warning\"][5]"));
 -	CHECK_XPATH_NODESET(n, STR("para[@type=\"warning\"][5]")) % 15;
 -}
 -
 -TEST_XML(xpath_paths_abbrev_w3c_19a, "<node><para/><para type='warning'/><para type='warning'/><para/><para type='error'/><para type='warning'/><para type='warning'/><para type='warning'/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("para[5][@type=\"warning\"]"));
 -	CHECK_XPATH_NODESET(n, STR("para[5][@type=\"warning\"]"));
 -}
 -
 -TEST_XML(xpath_paths_abbrev_w3c_19b, "<node><para/><para type='warning'/><para type='warning'/><para/><para type='warning'/><para type='warning'/><para type='warning'/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("para[5][@type=\"warning\"]"));
 -	CHECK_XPATH_NODESET(n, STR("para[5][@type=\"warning\"]")) % 9;
 -}
 -
 -TEST_XML(xpath_paths_abbrev_w3c_20, "<node><chapter><title>foo</title></chapter><chapter><title>Introduction</title></chapter><chapter><title>introduction</title></chapter><chapter/><chapter><title>Introduction</title><title>foo</title></chapter></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("chapter[title=\"Introduction\"]"));
 -	CHECK_XPATH_NODESET(n, STR("chapter[title=\"Introduction\"]")) % 6 % 13;
 -}
 -
 -TEST_XML(xpath_paths_abbrev_w3c_21, "<node><chapter><title>foo</title></chapter><chapter><title>Introduction</title></chapter><chapter><title>introduction</title></chapter><chapter/><chapter><title>Introduction</title><title>foo</title></chapter></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("chapter[title]"));
 -	CHECK_XPATH_NODESET(n, STR("chapter[title]")) % 3 % 6 % 9 % 13;
 -}
 -
 -TEST_XML(xpath_paths_abbrev_w3c_22, "<node><employee/><employee secretary=''/><employee assistant=''/><employee secretary='' assistant=''/><employee assistant='' secretary=''/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("employee[@secretary and @assistant]"));
 -	CHECK_XPATH_NODESET(n, STR("employee[@secretary and @assistant]")) % 8 % 11;
 -}
 -
 -#endif
 +#ifndef PUGIXML_NO_XPATH + +#include "common.hpp" + +TEST_XML(xpath_paths_abbrev_w3c_1, "<node><para/><foo/><para/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("para")); +	CHECK_XPATH_NODESET(n, STR("para")) % 3 % 5; +} + +TEST_XML(xpath_paths_abbrev_w3c_2, "<node><para/><foo/><para/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("*")); +	CHECK_XPATH_NODESET(n, STR("*")) % 3 % 4 % 5; +} + +TEST_XML(xpath_paths_abbrev_w3c_3, "<node>pcdata<child/><![CDATA[cdata]]></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("text()")); +	CHECK_XPATH_NODESET(n, STR("text()")) % 3 % 5; +} + +TEST_XML(xpath_paths_abbrev_w3c_4, "<node name='value' foo='bar' />") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("@name")); +	CHECK_XPATH_NODESET(n, STR("@name")) % 3; +} + +TEST_XML(xpath_paths_abbrev_w3c_5, "<node name='value' foo='bar' />") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("@*")); +	CHECK_XPATH_NODESET(n, STR("@*")) % 3 % 4; +} + +TEST_XML(xpath_paths_abbrev_w3c_6, "<node><para/><para/><para/><para/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("para[1]")); +	CHECK_XPATH_NODESET(n, STR("para[1]")) % 3; +} + +TEST_XML(xpath_paths_abbrev_w3c_7, "<node><para/><para/><para/><para/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("para[last()]")); +	CHECK_XPATH_NODESET(n, STR("para[last()]")) % 6; +} + +TEST_XML(xpath_paths_abbrev_w3c_8, "<node><para><para/><para/><foo><para/></foo></para><foo/><para/></node>") +{ +	xml_node c; + +	CHECK_XPATH_NODESET(c, STR("*/para")); +	CHECK_XPATH_NODESET(doc, STR("*/para")) % 3 % 9; +} + +TEST_XML(xpath_paths_abbrev_w3c_9, "<doc><chapter/><chapter/><chapter/><chapter/><chapter><section/><section/><section/></chapter><chapter/></doc>") +{ +	xml_node c; +	xml_node n = doc.child(STR("doc")).child(STR("chapter")); + +	CHECK_XPATH_NODESET(c, STR("/doc/chapter[5]/section[2]")); +	CHECK_XPATH_NODESET(n, STR("/doc/chapter[5]/section[2]")) % 9; +	CHECK_XPATH_NODESET(doc, STR("/doc/chapter[5]/section[2]")) % 9; +} + +TEST_XML(xpath_paths_abbrev_w3c_10, "<chapter><para><para/><para/><foo><para/></foo></para><foo/><para/></chapter>") +{ +	xml_node c; + +	CHECK_XPATH_NODESET(c, STR("chapter//para")); +	CHECK_XPATH_NODESET(doc, STR("chapter//para")) % 3 % 4 % 5 % 7 % 9; +} + +TEST_XML(xpath_paths_abbrev_w3c_11, "<node><para><para/><para/><foo><para/></foo></para><foo/><para/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("//para")); +	CHECK_XPATH_NODESET(n, STR("//para")) % 3 % 4 % 5 % 7 % 9; +	CHECK_XPATH_NODESET(n.child(STR("para")), STR("//para")) % 3 % 4 % 5 % 7 % 9; +} + +TEST_XML(xpath_paths_abbrev_w3c_12, "<node><olist><item/></olist><item/><olist><olist><item/><item/></olist></olist></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("//olist/item")); +	CHECK_XPATH_NODESET(n, STR("//olist/item")) % 4 % 8 % 9; +	CHECK_XPATH_NODESET(n.child(STR("olist")), STR("//olist/item")) % 4 % 8 % 9; +} + +TEST_XML(xpath_paths_abbrev_w3c_13, "<node><child/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR(".")); +	CHECK_XPATH_NODESET(n, STR(".")) % 2; +	CHECK_XPATH_NODESET(n.child(STR("child")), STR(".")) % 3; +} + +TEST_XML(xpath_paths_abbrev_w3c_14, "<node><para><para/><para/><foo><para/></foo></para><foo/><para/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR(".//para")); +	CHECK_XPATH_NODESET(n, STR(".//para")) % 3 % 4 % 5 % 7 % 9; +	CHECK_XPATH_NODESET(n.child(STR("para")), STR(".//para")) % 4 % 5 % 7; +} + +TEST_XML(xpath_paths_abbrev_w3c_15, "<node lang='en'><child/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("..")); +	CHECK_XPATH_NODESET(n, STR("..")) % 1; +	CHECK_XPATH_NODESET(n.child(STR("child")), STR("..")) % 2; +} + +TEST_XML(xpath_paths_abbrev_w3c_16, "<node lang='en'><child/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("../@lang")); +	CHECK_XPATH_NODESET(n, STR("../@lang")); +	CHECK_XPATH_NODESET(n.child(STR("child")), STR("../@lang")) % 3; +} + +TEST_XML(xpath_paths_abbrev_w3c_17, "<node><para/><para type='warning'/><para type='warning'/><para/><para type='error'/><para type='warning'/><para type='warning'/><para type='warning'/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("para[@type=\"warning\"]")); +	CHECK_XPATH_NODESET(n, STR("para[@type=\"warning\"]")) % 4 % 6 % 11 % 13 % 15; +} + +TEST_XML(xpath_paths_abbrev_w3c_18, "<node><para/><para type='warning'/><para type='warning'/><para/><para type='error'/><para type='warning'/><para type='warning'/><para type='warning'/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("para[@type=\"warning\"][5]")); +	CHECK_XPATH_NODESET(n, STR("para[@type=\"warning\"][5]")) % 15; +} + +TEST_XML(xpath_paths_abbrev_w3c_19a, "<node><para/><para type='warning'/><para type='warning'/><para/><para type='error'/><para type='warning'/><para type='warning'/><para type='warning'/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("para[5][@type=\"warning\"]")); +	CHECK_XPATH_NODESET(n, STR("para[5][@type=\"warning\"]")); +} + +TEST_XML(xpath_paths_abbrev_w3c_19b, "<node><para/><para type='warning'/><para type='warning'/><para/><para type='warning'/><para type='warning'/><para type='warning'/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("para[5][@type=\"warning\"]")); +	CHECK_XPATH_NODESET(n, STR("para[5][@type=\"warning\"]")) % 9; +} + +TEST_XML(xpath_paths_abbrev_w3c_20, "<node><chapter><title>foo</title></chapter><chapter><title>Introduction</title></chapter><chapter><title>introduction</title></chapter><chapter/><chapter><title>Introduction</title><title>foo</title></chapter></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("chapter[title=\"Introduction\"]")); +	CHECK_XPATH_NODESET(n, STR("chapter[title=\"Introduction\"]")) % 6 % 13; +} + +TEST_XML(xpath_paths_abbrev_w3c_21, "<node><chapter><title>foo</title></chapter><chapter><title>Introduction</title></chapter><chapter><title>introduction</title></chapter><chapter/><chapter><title>Introduction</title><title>foo</title></chapter></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("chapter[title]")); +	CHECK_XPATH_NODESET(n, STR("chapter[title]")) % 3 % 6 % 9 % 13; +} + +TEST_XML(xpath_paths_abbrev_w3c_22, "<node><employee/><employee secretary=''/><employee assistant=''/><employee secretary='' assistant=''/><employee assistant='' secretary=''/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("employee[@secretary and @assistant]")); +	CHECK_XPATH_NODESET(n, STR("employee[@secretary and @assistant]")) % 8 % 11; +} + +#endif diff --git a/tests/test_xpath_paths_w3c.cpp b/tests/test_xpath_paths_w3c.cpp index d3f1554..2005bc5 100644 --- a/tests/test_xpath_paths_w3c.cpp +++ b/tests/test_xpath_paths_w3c.cpp @@ -1,310 +1,310 @@ -#ifndef PUGIXML_NO_XPATH
 -
 -#include "common.hpp"
 -
 -TEST_XML(xpath_paths_w3c_1, "<node><para/><foo/><para/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("child::para"));
 -	CHECK_XPATH_NODESET(n, STR("child::para")) % 3 % 5;
 -}
 -
 -TEST_XML(xpath_paths_w3c_2, "<node><para/><foo/><para/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("child::*"));
 -	CHECK_XPATH_NODESET(n, STR("child::*")) % 3 % 4 % 5;
 -}
 -
 -TEST_XML(xpath_paths_w3c_3, "<node>pcdata<child/><![CDATA[cdata]]></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("child::text()"));
 -	CHECK_XPATH_NODESET(n, STR("child::text()")) % 3 % 5;
 -}
 -
 -TEST_XML(xpath_paths_w3c_4, "<node>pcdata<child/><![CDATA[cdata]]></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("child::node()"));
 -	CHECK_XPATH_NODESET(n, STR("child::node()")) % 3 % 4 % 5;
 -}
 -
 -TEST_XML(xpath_paths_w3c_5, "<node name='value' foo='bar' />")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("attribute::name"));
 -	CHECK_XPATH_NODESET(n, STR("attribute::name")) % 3;
 -}
 -
 -TEST_XML(xpath_paths_w3c_6, "<node name='value' foo='bar' />")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("attribute::*"));
 -	CHECK_XPATH_NODESET(n, STR("attribute::*")) % 3 % 4;
 -}
 -
 -TEST_XML(xpath_paths_w3c_7, "<node><para><para/><para/><foo><para/></foo></para><foo/><para/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("descendant::para"));
 -	CHECK_XPATH_NODESET(n, STR("descendant::para")) % 3 % 4 % 5 % 7 % 9;
 -	CHECK_XPATH_NODESET(n.child(STR("para")), STR("descendant::para")) % 4 % 5 % 7;
 -}
 -
 -TEST_XML(xpath_paths_w3c_8, "<node><div><font><div><div/></div></font></div></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("ancestor::div"));
 -	CHECK_XPATH_NODESET(n.child(STR("div")).child(STR("font")).child(STR("div")).child(STR("div")), STR("ancestor::div")) % 5 % 3;
 -}
 -
 -TEST_XML(xpath_paths_w3c_9, "<node><div><font><div><div/></div></font></div></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("ancestor-or-self::div"));
 -	CHECK_XPATH_NODESET(n.child(STR("div")).child(STR("font")).child(STR("div")).child(STR("div")), STR("ancestor-or-self::div")) % 6 % 5 % 3;
 -}
 -
 -TEST_XML(xpath_paths_w3c_10, "<node><para><para/><para/><foo><para/></foo></para><foo/><para/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("descendant-or-self::para"));
 -	CHECK_XPATH_NODESET(n, STR("descendant-or-self::para")) % 3 % 4 % 5 % 7 % 9;
 -	CHECK_XPATH_NODESET(n.child(STR("para")), STR("descendant-or-self::para")) % 3 % 4 % 5 % 7;
 -}
 -
 -TEST_XML(xpath_paths_w3c_11, "<node><para><para/><para/><foo><para/></foo></para><foo/><para/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("self::para"));
 -	CHECK_XPATH_NODESET(n, STR("self::para"));
 -	CHECK_XPATH_NODESET(n.child(STR("para")), STR("self::para")) % 3;
 -}
 -
 -TEST_XML(xpath_paths_w3c_12, "<chapter><para><para/><para/><foo><para/></foo></para><foo/><para/></chapter>")
 -{
 -	xml_node c;
 -
 -	CHECK_XPATH_NODESET(c, STR("child::chapter/descendant::para"));
 -	CHECK_XPATH_NODESET(doc, STR("child::chapter/descendant::para")) % 3 % 4 % 5 % 7 % 9;
 -}
 -
 -TEST_XML(xpath_paths_w3c_13, "<node><para><para/><para/><foo><para/></foo></para><foo/><para/></node>")
 -{
 -	xml_node c;
 -
 -	CHECK_XPATH_NODESET(c, STR("child::*/child::para"));
 -	CHECK_XPATH_NODESET(doc, STR("child::*/child::para")) % 3 % 9;
 -}
 -
 -TEST_XML(xpath_paths_w3c_14, "<node><para><para/><para/><foo><para/></foo></para><foo/><para/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("/"));
 -
 -	CHECK_XPATH_NODESET(doc, STR("/")) % 1;
 -	CHECK_XPATH_NODESET(n, STR("/")) % 1;
 -	CHECK_XPATH_NODESET(n.child(STR("para")), STR("/")) % 1;
 -}
 -
 -TEST_XML(xpath_paths_w3c_15, "<node><para><para/><para/><foo><para/></foo></para><foo/><para/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("/descendant::para"));
 -	CHECK_XPATH_NODESET(n, STR("/descendant::para")) % 3 % 4 % 5 % 7 % 9;
 -	CHECK_XPATH_NODESET(n.child(STR("para")), STR("/descendant::para")) % 3 % 4 % 5 % 7 % 9;
 -}
 -
 -TEST_XML(xpath_paths_w3c_16, "<node><olist><item/></olist><item/><olist><olist><item/><item/></olist></olist></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("/descendant::olist/child::item"));
 -	CHECK_XPATH_NODESET(n, STR("/descendant::olist/child::item")) % 4 % 8 % 9;
 -	CHECK_XPATH_NODESET(n.child(STR("olist")), STR("/descendant::olist/child::item")) % 4 % 8 % 9;
 -}
 -
 -TEST_XML(xpath_paths_w3c_17, "<node><para/><para/><para/><para/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("child::para[position()=1]"));
 -	CHECK_XPATH_NODESET(n, STR("child::para[position()=1]")) % 3;
 -}
 -
 -TEST_XML(xpath_paths_w3c_18, "<node><para/><para/><para/><para/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("child::para[position()=last()]"));
 -	CHECK_XPATH_NODESET(n, STR("child::para[position()=last()]")) % 6;
 -}
 -
 -TEST_XML(xpath_paths_w3c_19, "<node><para/><para/><para/><para/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("child::para[position()=last()-1]"));
 -	CHECK_XPATH_NODESET(n, STR("child::para[position()=last()-1]")) % 5;
 -}
 -
 -TEST_XML(xpath_paths_w3c_20, "<node><para/><para/><para/><para/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("child::para[position()>1]"));
 -	CHECK_XPATH_NODESET(n, STR("child::para[position()>1]")) % 4 % 5 % 6;
 -}
 -
 -TEST_XML(xpath_paths_w3c_21, "<node><chapter/><chapter/><chapter/><chapter/><chapter/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node")).child(STR("chapter")).next_sibling().next_sibling();
 -
 -	CHECK_XPATH_NODESET(c, STR("following-sibling::chapter[position()=1]"));
 -	CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[position()=1]")) % 6;
 -}
 -
 -TEST_XML(xpath_paths_w3c_22, "<node><chapter/><chapter/><chapter/><chapter/><chapter/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node")).child(STR("chapter")).next_sibling().next_sibling();
 -
 -	CHECK_XPATH_NODESET(c, STR("preceding-sibling::chapter[position()=1]"));
 -	CHECK_XPATH_NODESET(n, STR("preceding-sibling::chapter[position()=1]")) % 4;
 -}
 -
 -TEST_XML(xpath_paths_w3c_23, "<node><figure><figure/><figure/><foo><figure/></foo></figure><foo/><figure/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("/descendant::figure[position()=4]"));
 -	CHECK_XPATH_NODESET(n, STR("/descendant::figure[position()=4]")) % 7;
 -	CHECK_XPATH_NODESET(n.child(STR("figure")), STR("/descendant::figure[position()=4]")) % 7;
 -}
 -
 -TEST_XML(xpath_paths_w3c_24, "<doc><chapter/><chapter/><chapter/><chapter/><chapter><section/><section/><section/></chapter><chapter/></doc>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("doc")).child(STR("chapter"));
 -
 -	CHECK_XPATH_NODESET(c, STR("/child::doc/child::chapter[position()=5]/child::section[position()=2]"));
 -	CHECK_XPATH_NODESET(n, STR("/child::doc/child::chapter[position()=5]/child::section[position()=2]")) % 9;
 -	CHECK_XPATH_NODESET(doc, STR("/child::doc/child::chapter[position()=5]/child::section[position()=2]")) % 9;
 -}
 -
 -TEST_XML(xpath_paths_w3c_25, "<node><para/><para type='warning'/><para type='warning'/><para/><para type='error'/><para type='warning'/><para type='warning'/><para type='warning'/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("child::para[attribute::type=\"warning\"]"));
 -	CHECK_XPATH_NODESET(n, STR("child::para[attribute::type=\"warning\"]")) % 4 % 6 % 11 % 13 % 15;
 -}
 -
 -TEST_XML(xpath_paths_w3c_26, "<node><para/><para type='warning'/><para type='warning'/><para/><para type='error'/><para type='warning'/><para type='warning'/><para type='warning'/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("child::para[attribute::type=\"warning\"][position()=5]"));
 -	CHECK_XPATH_NODESET(n, STR("child::para[attribute::type=\"warning\"][position()=5]")) % 15;
 -}
 -
 -TEST_XML(xpath_paths_w3c_27a, "<node><para/><para type='warning'/><para type='warning'/><para/><para type='error'/><para type='warning'/><para type='warning'/><para type='warning'/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("child::para[position()=5][attribute::type=\"warning\"]"));
 -	CHECK_XPATH_NODESET(n, STR("child::para[position()=5][attribute::type=\"warning\"]"));
 -}
 -
 -TEST_XML(xpath_paths_w3c_27b, "<node><para/><para type='warning'/><para type='warning'/><para/><para type='warning'/><para type='warning'/><para type='warning'/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("child::para[position()=5][attribute::type=\"warning\"]"));
 -	CHECK_XPATH_NODESET(n, STR("child::para[position()=5][attribute::type=\"warning\"]")) % 9;
 -}
 -
 -TEST_XML(xpath_paths_w3c_28, "<node><chapter><title>foo</title></chapter><chapter><title>Introduction</title></chapter><chapter><title>introduction</title></chapter><chapter/><chapter><title>Introduction</title><title>foo</title></chapter></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("child::chapter[child::title='Introduction']"));
 -	CHECK_XPATH_NODESET(n, STR("child::chapter[child::title='Introduction']")) % 6 % 13;
 -}
 -
 -TEST_XML(xpath_paths_w3c_29, "<node><chapter><title>foo</title></chapter><chapter><title>Introduction</title></chapter><chapter><title>introduction</title></chapter><chapter/><chapter><title>Introduction</title><title>foo</title></chapter></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("child::chapter[child::title]"));
 -	CHECK_XPATH_NODESET(n, STR("child::chapter[child::title]")) % 3 % 6 % 9 % 13;
 -}
 -
 -TEST_XML(xpath_paths_w3c_30, "<node><abstract/><chapter/><chapter/><references/><appendix/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("child::*[self::chapter or self::appendix]"));
 -	CHECK_XPATH_NODESET(n, STR("child::*[self::chapter or self::appendix]")) % 4 % 5 % 7;
 -}
 -
 -TEST_XML(xpath_paths_w3c_31a, "<node><abstract/><chapter/><chapter/><references/><appendix/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("child::*[self::chapter or self::appendix][position()=last()]"));
 -	CHECK_XPATH_NODESET(n, STR("child::*[self::chapter or self::appendix][position()=last()]")) % 7;
 -}
 -
 -TEST_XML(xpath_paths_w3c_31b, "<node><abstract/><chapter/><chapter/><references/><appendix/><chapter/></node>")
 -{
 -	xml_node c;
 -	xml_node n = doc.child(STR("node"));
 -
 -	CHECK_XPATH_NODESET(c, STR("child::*[self::chapter or self::appendix][position()=last()]"));
 -	CHECK_XPATH_NODESET(n, STR("child::*[self::chapter or self::appendix][position()=last()]")) % 8;
 -}
 -
 -#endif
 +#ifndef PUGIXML_NO_XPATH + +#include "common.hpp" + +TEST_XML(xpath_paths_w3c_1, "<node><para/><foo/><para/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("child::para")); +	CHECK_XPATH_NODESET(n, STR("child::para")) % 3 % 5; +} + +TEST_XML(xpath_paths_w3c_2, "<node><para/><foo/><para/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("child::*")); +	CHECK_XPATH_NODESET(n, STR("child::*")) % 3 % 4 % 5; +} + +TEST_XML(xpath_paths_w3c_3, "<node>pcdata<child/><![CDATA[cdata]]></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("child::text()")); +	CHECK_XPATH_NODESET(n, STR("child::text()")) % 3 % 5; +} + +TEST_XML(xpath_paths_w3c_4, "<node>pcdata<child/><![CDATA[cdata]]></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("child::node()")); +	CHECK_XPATH_NODESET(n, STR("child::node()")) % 3 % 4 % 5; +} + +TEST_XML(xpath_paths_w3c_5, "<node name='value' foo='bar' />") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("attribute::name")); +	CHECK_XPATH_NODESET(n, STR("attribute::name")) % 3; +} + +TEST_XML(xpath_paths_w3c_6, "<node name='value' foo='bar' />") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("attribute::*")); +	CHECK_XPATH_NODESET(n, STR("attribute::*")) % 3 % 4; +} + +TEST_XML(xpath_paths_w3c_7, "<node><para><para/><para/><foo><para/></foo></para><foo/><para/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("descendant::para")); +	CHECK_XPATH_NODESET(n, STR("descendant::para")) % 3 % 4 % 5 % 7 % 9; +	CHECK_XPATH_NODESET(n.child(STR("para")), STR("descendant::para")) % 4 % 5 % 7; +} + +TEST_XML(xpath_paths_w3c_8, "<node><div><font><div><div/></div></font></div></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("ancestor::div")); +	CHECK_XPATH_NODESET(n.child(STR("div")).child(STR("font")).child(STR("div")).child(STR("div")), STR("ancestor::div")) % 5 % 3; +} + +TEST_XML(xpath_paths_w3c_9, "<node><div><font><div><div/></div></font></div></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("ancestor-or-self::div")); +	CHECK_XPATH_NODESET(n.child(STR("div")).child(STR("font")).child(STR("div")).child(STR("div")), STR("ancestor-or-self::div")) % 6 % 5 % 3; +} + +TEST_XML(xpath_paths_w3c_10, "<node><para><para/><para/><foo><para/></foo></para><foo/><para/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("descendant-or-self::para")); +	CHECK_XPATH_NODESET(n, STR("descendant-or-self::para")) % 3 % 4 % 5 % 7 % 9; +	CHECK_XPATH_NODESET(n.child(STR("para")), STR("descendant-or-self::para")) % 3 % 4 % 5 % 7; +} + +TEST_XML(xpath_paths_w3c_11, "<node><para><para/><para/><foo><para/></foo></para><foo/><para/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("self::para")); +	CHECK_XPATH_NODESET(n, STR("self::para")); +	CHECK_XPATH_NODESET(n.child(STR("para")), STR("self::para")) % 3; +} + +TEST_XML(xpath_paths_w3c_12, "<chapter><para><para/><para/><foo><para/></foo></para><foo/><para/></chapter>") +{ +	xml_node c; + +	CHECK_XPATH_NODESET(c, STR("child::chapter/descendant::para")); +	CHECK_XPATH_NODESET(doc, STR("child::chapter/descendant::para")) % 3 % 4 % 5 % 7 % 9; +} + +TEST_XML(xpath_paths_w3c_13, "<node><para><para/><para/><foo><para/></foo></para><foo/><para/></node>") +{ +	xml_node c; + +	CHECK_XPATH_NODESET(c, STR("child::*/child::para")); +	CHECK_XPATH_NODESET(doc, STR("child::*/child::para")) % 3 % 9; +} + +TEST_XML(xpath_paths_w3c_14, "<node><para><para/><para/><foo><para/></foo></para><foo/><para/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("/")); + +	CHECK_XPATH_NODESET(doc, STR("/")) % 1; +	CHECK_XPATH_NODESET(n, STR("/")) % 1; +	CHECK_XPATH_NODESET(n.child(STR("para")), STR("/")) % 1; +} + +TEST_XML(xpath_paths_w3c_15, "<node><para><para/><para/><foo><para/></foo></para><foo/><para/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("/descendant::para")); +	CHECK_XPATH_NODESET(n, STR("/descendant::para")) % 3 % 4 % 5 % 7 % 9; +	CHECK_XPATH_NODESET(n.child(STR("para")), STR("/descendant::para")) % 3 % 4 % 5 % 7 % 9; +} + +TEST_XML(xpath_paths_w3c_16, "<node><olist><item/></olist><item/><olist><olist><item/><item/></olist></olist></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("/descendant::olist/child::item")); +	CHECK_XPATH_NODESET(n, STR("/descendant::olist/child::item")) % 4 % 8 % 9; +	CHECK_XPATH_NODESET(n.child(STR("olist")), STR("/descendant::olist/child::item")) % 4 % 8 % 9; +} + +TEST_XML(xpath_paths_w3c_17, "<node><para/><para/><para/><para/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("child::para[position()=1]")); +	CHECK_XPATH_NODESET(n, STR("child::para[position()=1]")) % 3; +} + +TEST_XML(xpath_paths_w3c_18, "<node><para/><para/><para/><para/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("child::para[position()=last()]")); +	CHECK_XPATH_NODESET(n, STR("child::para[position()=last()]")) % 6; +} + +TEST_XML(xpath_paths_w3c_19, "<node><para/><para/><para/><para/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("child::para[position()=last()-1]")); +	CHECK_XPATH_NODESET(n, STR("child::para[position()=last()-1]")) % 5; +} + +TEST_XML(xpath_paths_w3c_20, "<node><para/><para/><para/><para/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("child::para[position()>1]")); +	CHECK_XPATH_NODESET(n, STR("child::para[position()>1]")) % 4 % 5 % 6; +} + +TEST_XML(xpath_paths_w3c_21, "<node><chapter/><chapter/><chapter/><chapter/><chapter/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")).child(STR("chapter")).next_sibling().next_sibling(); + +	CHECK_XPATH_NODESET(c, STR("following-sibling::chapter[position()=1]")); +	CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[position()=1]")) % 6; +} + +TEST_XML(xpath_paths_w3c_22, "<node><chapter/><chapter/><chapter/><chapter/><chapter/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")).child(STR("chapter")).next_sibling().next_sibling(); + +	CHECK_XPATH_NODESET(c, STR("preceding-sibling::chapter[position()=1]")); +	CHECK_XPATH_NODESET(n, STR("preceding-sibling::chapter[position()=1]")) % 4; +} + +TEST_XML(xpath_paths_w3c_23, "<node><figure><figure/><figure/><foo><figure/></foo></figure><foo/><figure/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("/descendant::figure[position()=4]")); +	CHECK_XPATH_NODESET(n, STR("/descendant::figure[position()=4]")) % 7; +	CHECK_XPATH_NODESET(n.child(STR("figure")), STR("/descendant::figure[position()=4]")) % 7; +} + +TEST_XML(xpath_paths_w3c_24, "<doc><chapter/><chapter/><chapter/><chapter/><chapter><section/><section/><section/></chapter><chapter/></doc>") +{ +	xml_node c; +	xml_node n = doc.child(STR("doc")).child(STR("chapter")); + +	CHECK_XPATH_NODESET(c, STR("/child::doc/child::chapter[position()=5]/child::section[position()=2]")); +	CHECK_XPATH_NODESET(n, STR("/child::doc/child::chapter[position()=5]/child::section[position()=2]")) % 9; +	CHECK_XPATH_NODESET(doc, STR("/child::doc/child::chapter[position()=5]/child::section[position()=2]")) % 9; +} + +TEST_XML(xpath_paths_w3c_25, "<node><para/><para type='warning'/><para type='warning'/><para/><para type='error'/><para type='warning'/><para type='warning'/><para type='warning'/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("child::para[attribute::type=\"warning\"]")); +	CHECK_XPATH_NODESET(n, STR("child::para[attribute::type=\"warning\"]")) % 4 % 6 % 11 % 13 % 15; +} + +TEST_XML(xpath_paths_w3c_26, "<node><para/><para type='warning'/><para type='warning'/><para/><para type='error'/><para type='warning'/><para type='warning'/><para type='warning'/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("child::para[attribute::type=\"warning\"][position()=5]")); +	CHECK_XPATH_NODESET(n, STR("child::para[attribute::type=\"warning\"][position()=5]")) % 15; +} + +TEST_XML(xpath_paths_w3c_27a, "<node><para/><para type='warning'/><para type='warning'/><para/><para type='error'/><para type='warning'/><para type='warning'/><para type='warning'/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("child::para[position()=5][attribute::type=\"warning\"]")); +	CHECK_XPATH_NODESET(n, STR("child::para[position()=5][attribute::type=\"warning\"]")); +} + +TEST_XML(xpath_paths_w3c_27b, "<node><para/><para type='warning'/><para type='warning'/><para/><para type='warning'/><para type='warning'/><para type='warning'/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("child::para[position()=5][attribute::type=\"warning\"]")); +	CHECK_XPATH_NODESET(n, STR("child::para[position()=5][attribute::type=\"warning\"]")) % 9; +} + +TEST_XML(xpath_paths_w3c_28, "<node><chapter><title>foo</title></chapter><chapter><title>Introduction</title></chapter><chapter><title>introduction</title></chapter><chapter/><chapter><title>Introduction</title><title>foo</title></chapter></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("child::chapter[child::title='Introduction']")); +	CHECK_XPATH_NODESET(n, STR("child::chapter[child::title='Introduction']")) % 6 % 13; +} + +TEST_XML(xpath_paths_w3c_29, "<node><chapter><title>foo</title></chapter><chapter><title>Introduction</title></chapter><chapter><title>introduction</title></chapter><chapter/><chapter><title>Introduction</title><title>foo</title></chapter></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("child::chapter[child::title]")); +	CHECK_XPATH_NODESET(n, STR("child::chapter[child::title]")) % 3 % 6 % 9 % 13; +} + +TEST_XML(xpath_paths_w3c_30, "<node><abstract/><chapter/><chapter/><references/><appendix/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("child::*[self::chapter or self::appendix]")); +	CHECK_XPATH_NODESET(n, STR("child::*[self::chapter or self::appendix]")) % 4 % 5 % 7; +} + +TEST_XML(xpath_paths_w3c_31a, "<node><abstract/><chapter/><chapter/><references/><appendix/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("child::*[self::chapter or self::appendix][position()=last()]")); +	CHECK_XPATH_NODESET(n, STR("child::*[self::chapter or self::appendix][position()=last()]")) % 7; +} + +TEST_XML(xpath_paths_w3c_31b, "<node><abstract/><chapter/><chapter/><references/><appendix/><chapter/></node>") +{ +	xml_node c; +	xml_node n = doc.child(STR("node")); + +	CHECK_XPATH_NODESET(c, STR("child::*[self::chapter or self::appendix][position()=last()]")); +	CHECK_XPATH_NODESET(n, STR("child::*[self::chapter or self::appendix][position()=last()]")) % 8; +} + +#endif diff --git a/tests/test_xpath_xalan_1.cpp b/tests/test_xpath_xalan_1.cpp index 7be711f..b862b1e 100644 --- a/tests/test_xpath_xalan_1.cpp +++ b/tests/test_xpath_xalan_1.cpp @@ -1,407 +1,407 @@ -#ifndef PUGIXML_NO_XPATH
 -
 -#include "common.hpp"
 -
 -TEST(xpath_xalan_boolean_1)
 -{
 -	xml_node c;
 -
 -	CHECK_XPATH_BOOLEAN(c, STR("true()"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("true() and true()"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("true() or true()"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("not(true())"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("boolean('')"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("1>2"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("1>=2"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("false()"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("1=1"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("1=2"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("1 = 1.00"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("0 = -0"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("1 = '001'"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("true()='0'"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("false()=''"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("true()=2"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("false()=0"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("false() and false()"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("'foo' and 'fop'"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("true() and false()"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("false() and true()"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("'1' and '0'"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("true() or false()"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("false() or true()"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("false() or false()"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("0 or ''"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("not(false())"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("not(false() = false())"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("not(true() = false())"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("not('')"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("not('0')"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("boolean('0')"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("boolean(0)"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("boolean(-0)"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("boolean(1)"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("boolean(1 div 0)"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("boolean(0 div 0)"), false);
 -}
 -
 -TEST_XML(xpath_xalan_boolean_2, "<doc/>")
 -{
 -	CHECK_XPATH_BOOLEAN(doc, STR("boolean(doc)"), true);
 -	CHECK_XPATH_BOOLEAN(doc, STR("boolean(foo)"), false);
 -}
 -
 -TEST(xpath_xalan_boolean_3)
 -{
 -	xml_node c;
 -
 -	CHECK_XPATH_BOOLEAN(c, STR("1>1"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("2>1"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("1<2"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("1<1"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("2<1"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("'2'>'1'"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("0 > -0"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("2>=2"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("2>=1"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("1<=2"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("1<=1"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("2<=1"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("false() and 1 div 0"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("true() or 1 div 0"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("1!=1"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("1!=2"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("1!=1.00"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("false()!=true()"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("true()!=false()"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("false()!=false()"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("'ace' != 'ace'"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("'ace' != 'abc'"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("'H' != '  H'"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("'H' != 'H  '"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("1.9999999 < 2.0"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("2.0000001 < 2.0"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("1.9999999 < 2"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("2 < 2.0"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("'001' = 1"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("0=false()"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("'0'=true()"), true);
 -}
 -
 -TEST_XML(xpath_xalan_boolean_4, "<avj><a>foo</a><b>bar</b><c>foobar</c><d>foo</d></avj>")
 -{
 -	CHECK_XPATH_BOOLEAN(doc, STR("avj/*='foo'"), true);
 -	CHECK_XPATH_BOOLEAN(doc, STR("not(avj/*='foo')"), false);
 -	CHECK_XPATH_BOOLEAN(doc, STR("avj/*!='foo'"), true);
 -	CHECK_XPATH_BOOLEAN(doc, STR("not(avj/*!='foo')"), false);
 -
 -	CHECK_XPATH_BOOLEAN(doc, STR("avj/k='foo'"), false);
 -	CHECK_XPATH_BOOLEAN(doc, STR("not(avj/k='foo')"), true);
 -	CHECK_XPATH_BOOLEAN(doc, STR("avj/k!='foo'"), false);
 -	CHECK_XPATH_BOOLEAN(doc, STR("not(avj/k!='foo')"), true);
 -}
 -
 -TEST_XML(xpath_xalan_boolean_5, "<doc><j l='12' w='33'>first</j><j l='17' w='45'>second</j><j l='16' w='78'>third</j><j l='12' w='33'>fourth</j></doc>")
 -{
 -	xml_node c = doc.child(STR("doc"));
 -
 -	CHECK_XPATH_BOOLEAN(c, STR("j[@l='12'] = j[@w='33']"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("j[@l='12'] = j[@l='17']"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("j[@l='12'] = j[.='first' or @w='45']"), true);
 -
 -	CHECK_XPATH_BOOLEAN(c, STR("j[@l='12'] != j[@w='33']"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("j[@l='12'] != j[@l='17']"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("j[@l='12'] != j[.='first' or @w='45']"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("j[@l='16'] != j[@w='78']"), false);
 -}
 -
 -TEST_XML(xpath_xalan_boolean_6, "<doc><avj><good><b>12</b><c>34</c><d>56</d><e>78</e></good></avj></doc>")
 -{
 -	xml_node c = doc.child(STR("doc"));
 -
 -	CHECK_XPATH_BOOLEAN(c, STR("avj/good/*=34"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("not(avj/good/*=34)"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("avj/good/*!=34"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("not(avj/good/*!=34)"), false);
 -
 -	CHECK_XPATH_BOOLEAN(c, STR("34=avj/good/*"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("not(34=avj/good/*)"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("34!=avj/good/*"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("not(34!=avj/good/*)"), false);
 -}
 -
 -TEST_XML(xpath_xalan_boolean_7, "<doc><avj><bool><b>true</b><c></c><d>false?</d><e>1</e><f>0</f></bool></avj></doc>")
 -{
 -	xml_node c = doc.child(STR("doc"));
 -
 -	CHECK_XPATH_BOOLEAN(c, STR("avj/bool/*=true()"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("not(avj/bool/*=true())"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("avj/bool/*!=true()"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("not(avj/bool/*!=true())"), true);
 -
 -	CHECK_XPATH_BOOLEAN(c, STR("true()=avj/bool/*"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("not(true()=avj/bool/*)"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("true()!=avj/bool/*"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("not(true()!=avj/bool/*)"), true);
 -
 -	CHECK_XPATH_BOOLEAN(c, STR("avj/none/*=true()"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("not(avj/none/*=true())"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("avj/none/*!=true()"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("not(avj/none/*!=true())"), false);
 -
 -	CHECK_XPATH_BOOLEAN(c, STR("true()=avj/none/*"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("not(true()=avj/none/*)"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("true()!=avj/none/*"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("not(true()!=avj/none/*)"), false);
 -}
 -
 -TEST_XML(xpath_xalan_conditional, "<letters>b</letters>")
 -{
 -	xml_node c;
 -
 -	CHECK_XPATH_BOOLEAN(c, STR("(round(3.7) > 3)"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("2 > 1"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("9 mod 3 = 0"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("'a'='a'"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("2+2=4"), true);
 -
 -	xml_node b = doc.child(STR("letters")).first_child();
 -
 -	CHECK_XPATH_BOOLEAN(b, STR(".='b'"), true);
 -	CHECK_XPATH_BOOLEAN(b, STR("name(..)='letters'"), true);
 -}
 -
 -TEST_XML(xpath_xalan_math_1, "<a>3</a>")
 -{
 -	xml_node c;
 -
 -	CHECK_XPATH_NUMBER(c, STR("number('1')"), 1);
 -	CHECK_XPATH_NUMBER(c, STR("floor(0.0)"), 0);
 -	CHECK_XPATH_NUMBER(c, STR("ceiling(0.0)"), 0);
 -	CHECK_XPATH_NUMBER(c, STR("round(0.0)"), 0);
 -	CHECK_XPATH_NUMBER(c, STR("2*3"), 6);
 -	CHECK_XPATH_NUMBER(c, STR("3+6"), 9);
 -	CHECK_XPATH_NUMBER(c, STR("3-1"), 2);
 -	CHECK_XPATH_NUMBER_NAN(doc, STR("a-1")); // a-1 is a name test, not arithmetic expression
 -	CHECK_XPATH_NUMBER(doc, STR("a -1"), 2);
 -	CHECK_XPATH_NUMBER(c, STR("6 div 2"), 3);
 -	CHECK_XPATH_NUMBER(c, STR("5 mod 2"), 1);
 -	CHECK_XPATH_NUMBER_NAN(c, STR("number(n)"));
 -	CHECK_XPATH_NUMBER(c, STR("number(2)"), 2);
 -	CHECK_XPATH_NUMBER(c, STR("number('3')"), 3);
 -	CHECK_XPATH_NUMBER_NAN(c, STR("number('')"));
 -	CHECK_XPATH_NUMBER_NAN(c, STR("number('abc')"));
 -	CHECK_XPATH_BOOLEAN(c, STR("number(string(1.0))=1"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("number(true())=1"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("number(false())=0"), true);
 -
 -#ifndef MSVC6_NAN_BUG
 -	CHECK_XPATH_BOOLEAN(c, STR("number('xxx')=number('xxx')"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("number('xxx')=0"), false);
 -#endif
 -
 -	CHECK_XPATH_NUMBER(doc, STR("floor(a)"), 3);
 -	CHECK_XPATH_NUMBER(c, STR("floor(1.9)"), 1);
 -	CHECK_XPATH_NUMBER(c, STR("floor(2.999999)"), 2);
 -	CHECK_XPATH_NUMBER(c, STR("floor(-1.5)"), -2);
 -	CHECK_XPATH_BOOLEAN(c, STR("floor(1)=1"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("floor(1.9)=1"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("floor(-1.5)=-2"), true);
 -	CHECK_XPATH_NUMBER(doc, STR("ceiling(a)"), 3);
 -	CHECK_XPATH_NUMBER(c, STR("ceiling(1.54)"), 2);
 -	CHECK_XPATH_NUMBER(c, STR("ceiling(2.999999)"), 3);
 -	CHECK_XPATH_NUMBER(c, STR("ceiling(3.000001)"), 4);
 -	CHECK_XPATH_BOOLEAN(c, STR("ceiling(1)=1"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("ceiling(1.1)=2"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("ceiling(-1.5)=-1"), true);
 -}
 -
 -TEST_XML(xpath_xalan_math_2, "<a>3</a>")
 -{
 -	xml_node c;
 -
 -	CHECK_XPATH_NUMBER(doc, STR("round(a)"), 3);
 -	CHECK_XPATH_NUMBER(c, STR("round(1.24)"), 1);
 -	CHECK_XPATH_NUMBER(c, STR("round(2.999999)"), 3);
 -	CHECK_XPATH_NUMBER(c, STR("round(3.000001)"), 3);
 -	CHECK_XPATH_NUMBER(c, STR("round(1.1)"), 1);
 -	CHECK_XPATH_NUMBER(c, STR("round(-1.1)"), -1);
 -	CHECK_XPATH_NUMBER(c, STR("round(1.9)"), 2);
 -	CHECK_XPATH_NUMBER(c, STR("round(-1.9)"), -2);
 -	CHECK_XPATH_NUMBER(c, STR("round(1.5)"), 2);
 -	CHECK_XPATH_NUMBER(c, STR("round(-1.5)"), -1);
 -	CHECK_XPATH_NUMBER(c, STR("round(1.4999999)"), 1);
 -	CHECK_XPATH_NUMBER(c, STR("round(-1.4999999)"), -1);
 -	CHECK_XPATH_NUMBER(c, STR("round(1.5000001)"), 2);
 -	CHECK_XPATH_NUMBER(c, STR("round(-1.5000001)"), -2);
 -}
 -
 -TEST_XML(xpath_xalan_math_3, "<doc><n v='1'/><n>2</n><n v='3'/><n>4</n><n v='5'>5</n><e>17</e><e>-5</e><e>8</e><e>-37</e></doc>")
 -{
 -	CHECK_XPATH_NUMBER(doc, STR("sum(doc/x)"), 0);
 -	CHECK_XPATH_NUMBER_NAN(doc, STR("sum(doc/n)"));
 -	CHECK_XPATH_NUMBER(doc, STR("sum(doc/n[text()])"), 11);
 -	CHECK_XPATH_NUMBER(doc, STR("sum(doc/n/@v)"), 9);
 -	CHECK_XPATH_NUMBER(doc, STR("sum(doc/e)"), -17);
 -}
 -
 -TEST_XML(xpath_xalan_math_4, "<doc><n1 a='1'>2</n1><n2 a='2'>3</n2><n1-n2>123</n1-n2><n-1>72</n-1><n-2>12</n-2><div a='2'>5</div><mod a='5'>2</mod></doc>")
 -{
 -	xml_node c = doc.child(STR("doc"));
 -
 -	CHECK_XPATH_NUMBER(c, STR("n1*n2"), 6);
 -	CHECK_XPATH_NUMBER(c, STR("n1/@a*n2/@a"), 2);
 -	CHECK_XPATH_NUMBER(c, STR("(n1/@a)*(n2/@a)"), 2);
 -	CHECK_XPATH_NUMBER(c, STR("n1+n2"), 5);
 -	CHECK_XPATH_NUMBER(c, STR("n1/@a+n2/@a"), 3);
 -	CHECK_XPATH_NUMBER(c, STR("(n1/@a)+(n2/@a)"), 3);
 -	CHECK_XPATH_NUMBER(c, STR("1-2"), -1);
 -	CHECK_XPATH_NUMBER(c, STR("n1 - n2"), -1);
 -	CHECK_XPATH_NUMBER(c, STR("n1-n2"), 123);
 -	CHECK_XPATH_NUMBER(c, STR("n-1 - n-2"), 60);
 -	CHECK_XPATH_NUMBER(c, STR("n-1 -n-2"), 60);
 -	CHECK_XPATH_NUMBER(c, STR("7+-3"), 4);
 -	CHECK_XPATH_NUMBER(c, STR("n-1+-n-2"), 60);
 -	CHECK_XPATH_NUMBER(c, STR("7 - -3"), 10);
 -	CHECK_XPATH_NUMBER(c, STR("n-1 - -n-2"), 84);
 -	CHECK_XPATH_NUMBER(c, STR("-7 --3"), -4);
 -	CHECK_XPATH_NUMBER(c, STR("-n-1 --n-2"), -60);
 -
 -	CHECK_XPATH_FAIL(STR("+7"));
 -	CHECK_XPATH_FAIL(STR("7++3"));
 -	CHECK_XPATH_FAIL(STR("7-+3"));
 -
 -	CHECK_XPATH_NUMBER(c, STR("6 div -2"), -3);
 -	CHECK_XPATH_NUMBER(c, STR("n1 div n2"), 2.0 / 3.0);
 -	CHECK_XPATH_NUMBER(c, STR("div div mod"), 2.5);
 -	CHECK_XPATH_NUMBER(c, STR("div/@a div mod/@a"), 0.4);
 -
 -	CHECK_XPATH_BOOLEAN(c, STR("1 div -0 = 2 div -0"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("1 div -0 = 1 div 0"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("1 div -0 = -1 div 0"), true);
 -
 -#ifndef MSVC6_NAN_BUG
 -	CHECK_XPATH_BOOLEAN(c, STR("0 div 0 >= 0"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("0 div 0 < 0"), false);
 -#endif
 -
 -	CHECK_XPATH_NUMBER(c, STR("n1 mod n2"), 2);
 -	CHECK_XPATH_NUMBER(c, STR("div mod mod"), 1);
 -	CHECK_XPATH_NUMBER(c, STR("div/@a mod mod/@a"), 2);
 -
 -	CHECK_XPATH_BOOLEAN(c, STR("(5 mod 2 = 1) and (5 mod -2 = 1) and (-5 mod 2 = -1) and (-5 mod -2 = -1)"), true);
 -}
 -
 -TEST(xpath_xalan_math_5)
 -{
 -	xml_node c;
 -
 -	CHECK_XPATH_NUMBER(c, STR("(((((('3'+5)*(3)+((('2')+2)*('1' - 6)))-('4' - '2'))+(-(4-6)))))"), 4);
 -	CHECK_XPATH_NUMBER(c, STR("1*1*2*2*2*3*3*1*1*1*0.5*0.5"), 18);
 -	CHECK_XPATH_NUMBER(c, STR("1440 div 2 div 2 div 6"), 60);
 -	CHECK_XPATH_NUMBER(c, STR("1440 div 2 div 2 div 6 div 10"), 6);
 -	CHECK_XPATH_NUMBER(c, STR("1440 div 2 div 2 div 6 div 10 div 3"), 2);
 -	CHECK_XPATH_NUMBER(c, STR("(1*2*3*4*5*6)div 2 div 6 div 10 div 3"), 2);
 -	CHECK_XPATH_NUMBER_NAN(c, STR("(2 + number('xxx'))"));
 -	CHECK_XPATH_NUMBER_NAN(c, STR("2 * -number('xxx')"));
 -	CHECK_XPATH_NUMBER_NAN(c, STR("2 - number('xxx')"));
 -	CHECK_XPATH_NUMBER_NAN(c, STR("number('xxx') - 3"));
 -	CHECK_XPATH_NUMBER_NAN(c, STR("2 div number('xxx')"));
 -	CHECK_XPATH_NUMBER_NAN(c, STR("number('xxx') div 3"));
 -
 -#ifndef __BORLANDC__ // BCC fmod does not propagate NaN correctly
 -	CHECK_XPATH_NUMBER_NAN(c, STR("2 mod number('xxx')"));
 -	CHECK_XPATH_NUMBER_NAN(c, STR("number('xxx') mod 3"));
 -#endif
 -
 -	CHECK_XPATH_NUMBER_NAN(c, STR("floor(number('xxx'))"));
 -	CHECK_XPATH_NUMBER_NAN(c, STR("ceiling(number('xxx'))"));
 -	CHECK_XPATH_NUMBER_NAN(c, STR("round(number('xxx'))"));
 -	CHECK_XPATH_NUMBER(c, STR("10+5+25+20+15+50+35+40"), 200);
 -	CHECK_XPATH_NUMBER(c, STR("100-9-7-4-17-18-5"), 40);
 -	CHECK_XPATH_NUMBER(c, STR("3*2+5*4-4*2-1"), 17);
 -    CHECK_XPATH_NUMBER(c, STR("6*5-8*2+5*2"), 24);
 -    CHECK_XPATH_NUMBER(c, STR("10*5-4*2+6*1 -3*3"), 39);
 -
 -    CHECK_XPATH_NUMBER(c, STR("(24 div 3 +2) div (40 div 8 -3)"), 5);
 -    CHECK_XPATH_NUMBER(c, STR("80 div 2 + 12 div 2 - 4 div 2"), 44);
 -    CHECK_XPATH_NUMBER(c, STR("70 div 10 - 18 div 6 + 10 div 2"), 9);
 -
 -    CHECK_XPATH_NUMBER(c, STR("48 mod 17 - 2 mod 9 + 13 mod 5"), 15);
 -    CHECK_XPATH_NUMBER(c, STR("56 mod round(5*2+1.444) - 6 mod 4 + 7 mod 4"), 2);
 -    CHECK_XPATH_NUMBER(c, STR("(77 mod 10 + 5 mod 8) mod 10"), 2);
 -}
 -
 -TEST_XML(xpath_xalan_math_6, "<doc><n1>3</n1><n2>7</n2><n3>x</n3></doc>")
 -{
 -	xml_node c = doc.child(STR("doc"));
 -
 -	CHECK_XPATH_NUMBER(c, STR("-(n1|n2)"), -3);
 -	CHECK_XPATH_NUMBER(c, STR("-(n2|n1)"), -3);
 -	CHECK_XPATH_BOOLEAN(c, STR("contains(number(n1), 'NaN')"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("contains(number(n3), 'NaN')"), true);
 -}
 -
 -TEST_XML(xpath_xalan_math_7, "<doc><n1>3</n1><n2>7</n2><n3>x</n3></doc>")
 -{
 -	xml_node c = doc.child(STR("doc"));
 -
 -	CHECK_XPATH_NUMBER(c, STR("-(n1|n2)"), -3);
 -	CHECK_XPATH_NUMBER(c, STR("-(n2|n1)"), -3);
 -	CHECK_XPATH_BOOLEAN(c, STR("contains(number(n1), 'NaN')"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("contains(number(n3), 'NaN')"), true);
 -}
 -
 -TEST_XML(xpath_xalan_math_8, "<k>0.0004</k>")
 -{
 -	CHECK_XPATH_NUMBER(doc, STR("number(1.75)"), 1.75);
 -	CHECK_XPATH_NUMBER(doc, STR("number(7 div 4)"), 1.75);
 -	CHECK_XPATH_BOOLEAN(doc, STR("(number(1.75) = (7 div 4))"), true);
 -	CHECK_XPATH_NUMBER(doc, STR("number(0.109375 * 16)"), 1.75);
 -	CHECK_XPATH_BOOLEAN(doc, STR("(number(1.75) = (0.109375 * 16))"), true);
 -	CHECK_XPATH_NUMBER(doc, STR("number(k)"), 0.0004);
 -	CHECK_XPATH_NUMBER(doc, STR("number(4 div 10000)"), 0.0004);
 -	CHECK_XPATH_BOOLEAN(doc, STR("(number(k) = (4 div 10000))"), true);
 -	CHECK_XPATH_NUMBER(doc, STR("number(0.0001 * 4)"), 0.0004);
 -	CHECK_XPATH_BOOLEAN(doc, STR("(number(k) = (0.0001 * 4))"), true);
 -}
 -
 -TEST(xpath_xalan_math_9)
 -{
 -	xml_node c;
 -
 -	CHECK_XPATH_STRING(c, STR("string(number('0.0'))"), STR("0"));
 -	CHECK_XPATH_STRING(c, STR("string(-1 * number('0.0'))"), STR("0"));
 -
 -	CHECK_XPATH_STRING(c, STR("string(number('0.4'))"), STR("0.4"));
 -	CHECK_XPATH_STRING(c, STR("string(-1 * number('0.4'))"), STR("-0.4"));
 -
 -	CHECK_XPATH_STRING(c, STR("string(number('4.0'))"), STR("4"));
 -	CHECK_XPATH_STRING(c, STR("string(-1 * number('4.0'))"), STR("-4"));
 -
 -	CHECK_XPATH_STRING(c, STR("string(number('0.04'))"), STR("0.04"));
 -	CHECK_XPATH_STRING(c, STR("string(-1 * number('0.04'))"), STR("-0.04"));
 -
 -	CHECK_XPATH_STRING(c, STR("string(number('0.004'))"), STR("0.004"));
 -	CHECK_XPATH_STRING(c, STR("string(-1 * number('0.004'))"), STR("-0.004"));
 -
 -	CHECK_XPATH_STRING(c, STR("string(number('0.0004'))"), STR("0.0004"));
 -	CHECK_XPATH_STRING(c, STR("string(-1 * number('0.0004'))"), STR("-0.0004"));
 -
 -	CHECK_XPATH_STRING(c, STR("string(number('0.0000000000001'))"), STR("0.0000000000001"));
 -	CHECK_XPATH_STRING(c, STR("string(-1 * number('0.0000000000001'))"), STR("-0.0000000000001"));
 -
 -	CHECK_XPATH_STRING(c, STR("string(number('0.0000000000000000000000000001'))"), STR("0.0000000000000000000000000001"));
 -	CHECK_XPATH_STRING(c, STR("string(-1 * number('0.0000000000000000000000000001'))"), STR("-0.0000000000000000000000000001"));
 -
 -	CHECK_XPATH_STRING(c, STR("string(number('0.0000000000001000000000000001'))"), STR("0.0000000000001000000000000001"));
 -	CHECK_XPATH_STRING(c, STR("string(-1 * number('0.0000000000001000000000000001'))"), STR("-0.0000000000001000000000000001"));
 -
 -	CHECK_XPATH_STRING(c, STR("string(number('0.0012'))"), STR("0.0012"));
 -	CHECK_XPATH_STRING(c, STR("string(-1 * number('0.0012'))"), STR("-0.0012"));
 -
 -	CHECK_XPATH_STRING(c, STR("string(number('0.012'))"), STR("0.012"));
 -	CHECK_XPATH_STRING(c, STR("string(-1 * number('0.012'))"), STR("-0.012"));
 -}
 -
 -#endif
 +#ifndef PUGIXML_NO_XPATH + +#include "common.hpp" + +TEST(xpath_xalan_boolean_1) +{ +	xml_node c; + +	CHECK_XPATH_BOOLEAN(c, STR("true()"), true); +	CHECK_XPATH_BOOLEAN(c, STR("true() and true()"), true); +	CHECK_XPATH_BOOLEAN(c, STR("true() or true()"), true); +	CHECK_XPATH_BOOLEAN(c, STR("not(true())"), false); +	CHECK_XPATH_BOOLEAN(c, STR("boolean('')"), false); +	CHECK_XPATH_BOOLEAN(c, STR("1>2"), false); +	CHECK_XPATH_BOOLEAN(c, STR("1>=2"), false); +	CHECK_XPATH_BOOLEAN(c, STR("false()"), false); +	CHECK_XPATH_BOOLEAN(c, STR("1=1"), true); +	CHECK_XPATH_BOOLEAN(c, STR("1=2"), false); +	CHECK_XPATH_BOOLEAN(c, STR("1 = 1.00"), true); +	CHECK_XPATH_BOOLEAN(c, STR("0 = -0"), true); +	CHECK_XPATH_BOOLEAN(c, STR("1 = '001'"), true); +	CHECK_XPATH_BOOLEAN(c, STR("true()='0'"), true); +	CHECK_XPATH_BOOLEAN(c, STR("false()=''"), true); +	CHECK_XPATH_BOOLEAN(c, STR("true()=2"), true); +	CHECK_XPATH_BOOLEAN(c, STR("false()=0"), true); +	CHECK_XPATH_BOOLEAN(c, STR("false() and false()"), false); +	CHECK_XPATH_BOOLEAN(c, STR("'foo' and 'fop'"), true); +	CHECK_XPATH_BOOLEAN(c, STR("true() and false()"), false); +	CHECK_XPATH_BOOLEAN(c, STR("false() and true()"), false); +	CHECK_XPATH_BOOLEAN(c, STR("'1' and '0'"), true); +	CHECK_XPATH_BOOLEAN(c, STR("true() or false()"), true); +	CHECK_XPATH_BOOLEAN(c, STR("false() or true()"), true); +	CHECK_XPATH_BOOLEAN(c, STR("false() or false()"), false); +	CHECK_XPATH_BOOLEAN(c, STR("0 or ''"), false); +	CHECK_XPATH_BOOLEAN(c, STR("not(false())"), true); +	CHECK_XPATH_BOOLEAN(c, STR("not(false() = false())"), false); +	CHECK_XPATH_BOOLEAN(c, STR("not(true() = false())"), true); +	CHECK_XPATH_BOOLEAN(c, STR("not('')"), true); +	CHECK_XPATH_BOOLEAN(c, STR("not('0')"), false); +	CHECK_XPATH_BOOLEAN(c, STR("boolean('0')"), true); +	CHECK_XPATH_BOOLEAN(c, STR("boolean(0)"), false); +	CHECK_XPATH_BOOLEAN(c, STR("boolean(-0)"), false); +	CHECK_XPATH_BOOLEAN(c, STR("boolean(1)"), true); +	CHECK_XPATH_BOOLEAN(c, STR("boolean(1 div 0)"), true); +	CHECK_XPATH_BOOLEAN(c, STR("boolean(0 div 0)"), false); +} + +TEST_XML(xpath_xalan_boolean_2, "<doc/>") +{ +	CHECK_XPATH_BOOLEAN(doc, STR("boolean(doc)"), true); +	CHECK_XPATH_BOOLEAN(doc, STR("boolean(foo)"), false); +} + +TEST(xpath_xalan_boolean_3) +{ +	xml_node c; + +	CHECK_XPATH_BOOLEAN(c, STR("1>1"), false); +	CHECK_XPATH_BOOLEAN(c, STR("2>1"), true); +	CHECK_XPATH_BOOLEAN(c, STR("1<2"), true); +	CHECK_XPATH_BOOLEAN(c, STR("1<1"), false); +	CHECK_XPATH_BOOLEAN(c, STR("2<1"), false); +	CHECK_XPATH_BOOLEAN(c, STR("'2'>'1'"), true); +	CHECK_XPATH_BOOLEAN(c, STR("0 > -0"), false); +	CHECK_XPATH_BOOLEAN(c, STR("2>=2"), true); +	CHECK_XPATH_BOOLEAN(c, STR("2>=1"), true); +	CHECK_XPATH_BOOLEAN(c, STR("1<=2"), true); +	CHECK_XPATH_BOOLEAN(c, STR("1<=1"), true); +	CHECK_XPATH_BOOLEAN(c, STR("2<=1"), false); +	CHECK_XPATH_BOOLEAN(c, STR("false() and 1 div 0"), false); +	CHECK_XPATH_BOOLEAN(c, STR("true() or 1 div 0"), true); +	CHECK_XPATH_BOOLEAN(c, STR("1!=1"), false); +	CHECK_XPATH_BOOLEAN(c, STR("1!=2"), true); +	CHECK_XPATH_BOOLEAN(c, STR("1!=1.00"), false); +	CHECK_XPATH_BOOLEAN(c, STR("false()!=true()"), true); +	CHECK_XPATH_BOOLEAN(c, STR("true()!=false()"), true); +	CHECK_XPATH_BOOLEAN(c, STR("false()!=false()"), false); +	CHECK_XPATH_BOOLEAN(c, STR("'ace' != 'ace'"), false); +	CHECK_XPATH_BOOLEAN(c, STR("'ace' != 'abc'"), true); +	CHECK_XPATH_BOOLEAN(c, STR("'H' != '  H'"), true); +	CHECK_XPATH_BOOLEAN(c, STR("'H' != 'H  '"), true); +	CHECK_XPATH_BOOLEAN(c, STR("1.9999999 < 2.0"), true); +	CHECK_XPATH_BOOLEAN(c, STR("2.0000001 < 2.0"), false); +	CHECK_XPATH_BOOLEAN(c, STR("1.9999999 < 2"), true); +	CHECK_XPATH_BOOLEAN(c, STR("2 < 2.0"), false); +	CHECK_XPATH_BOOLEAN(c, STR("'001' = 1"), true); +	CHECK_XPATH_BOOLEAN(c, STR("0=false()"), true); +	CHECK_XPATH_BOOLEAN(c, STR("'0'=true()"), true); +} + +TEST_XML(xpath_xalan_boolean_4, "<avj><a>foo</a><b>bar</b><c>foobar</c><d>foo</d></avj>") +{ +	CHECK_XPATH_BOOLEAN(doc, STR("avj/*='foo'"), true); +	CHECK_XPATH_BOOLEAN(doc, STR("not(avj/*='foo')"), false); +	CHECK_XPATH_BOOLEAN(doc, STR("avj/*!='foo'"), true); +	CHECK_XPATH_BOOLEAN(doc, STR("not(avj/*!='foo')"), false); + +	CHECK_XPATH_BOOLEAN(doc, STR("avj/k='foo'"), false); +	CHECK_XPATH_BOOLEAN(doc, STR("not(avj/k='foo')"), true); +	CHECK_XPATH_BOOLEAN(doc, STR("avj/k!='foo'"), false); +	CHECK_XPATH_BOOLEAN(doc, STR("not(avj/k!='foo')"), true); +} + +TEST_XML(xpath_xalan_boolean_5, "<doc><j l='12' w='33'>first</j><j l='17' w='45'>second</j><j l='16' w='78'>third</j><j l='12' w='33'>fourth</j></doc>") +{ +	xml_node c = doc.child(STR("doc")); + +	CHECK_XPATH_BOOLEAN(c, STR("j[@l='12'] = j[@w='33']"), true); +	CHECK_XPATH_BOOLEAN(c, STR("j[@l='12'] = j[@l='17']"), false); +	CHECK_XPATH_BOOLEAN(c, STR("j[@l='12'] = j[.='first' or @w='45']"), true); + +	CHECK_XPATH_BOOLEAN(c, STR("j[@l='12'] != j[@w='33']"), true); +	CHECK_XPATH_BOOLEAN(c, STR("j[@l='12'] != j[@l='17']"), true); +	CHECK_XPATH_BOOLEAN(c, STR("j[@l='12'] != j[.='first' or @w='45']"), true); +	CHECK_XPATH_BOOLEAN(c, STR("j[@l='16'] != j[@w='78']"), false); +} + +TEST_XML(xpath_xalan_boolean_6, "<doc><avj><good><b>12</b><c>34</c><d>56</d><e>78</e></good></avj></doc>") +{ +	xml_node c = doc.child(STR("doc")); + +	CHECK_XPATH_BOOLEAN(c, STR("avj/good/*=34"), true); +	CHECK_XPATH_BOOLEAN(c, STR("not(avj/good/*=34)"), false); +	CHECK_XPATH_BOOLEAN(c, STR("avj/good/*!=34"), true); +	CHECK_XPATH_BOOLEAN(c, STR("not(avj/good/*!=34)"), false); + +	CHECK_XPATH_BOOLEAN(c, STR("34=avj/good/*"), true); +	CHECK_XPATH_BOOLEAN(c, STR("not(34=avj/good/*)"), false); +	CHECK_XPATH_BOOLEAN(c, STR("34!=avj/good/*"), true); +	CHECK_XPATH_BOOLEAN(c, STR("not(34!=avj/good/*)"), false); +} + +TEST_XML(xpath_xalan_boolean_7, "<doc><avj><bool><b>true</b><c></c><d>false?</d><e>1</e><f>0</f></bool></avj></doc>") +{ +	xml_node c = doc.child(STR("doc")); + +	CHECK_XPATH_BOOLEAN(c, STR("avj/bool/*=true()"), true); +	CHECK_XPATH_BOOLEAN(c, STR("not(avj/bool/*=true())"), false); +	CHECK_XPATH_BOOLEAN(c, STR("avj/bool/*!=true()"), false); +	CHECK_XPATH_BOOLEAN(c, STR("not(avj/bool/*!=true())"), true); + +	CHECK_XPATH_BOOLEAN(c, STR("true()=avj/bool/*"), true); +	CHECK_XPATH_BOOLEAN(c, STR("not(true()=avj/bool/*)"), false); +	CHECK_XPATH_BOOLEAN(c, STR("true()!=avj/bool/*"), false); +	CHECK_XPATH_BOOLEAN(c, STR("not(true()!=avj/bool/*)"), true); + +	CHECK_XPATH_BOOLEAN(c, STR("avj/none/*=true()"), false); +	CHECK_XPATH_BOOLEAN(c, STR("not(avj/none/*=true())"), true); +	CHECK_XPATH_BOOLEAN(c, STR("avj/none/*!=true()"), true); +	CHECK_XPATH_BOOLEAN(c, STR("not(avj/none/*!=true())"), false); + +	CHECK_XPATH_BOOLEAN(c, STR("true()=avj/none/*"), false); +	CHECK_XPATH_BOOLEAN(c, STR("not(true()=avj/none/*)"), true); +	CHECK_XPATH_BOOLEAN(c, STR("true()!=avj/none/*"), true); +	CHECK_XPATH_BOOLEAN(c, STR("not(true()!=avj/none/*)"), false); +} + +TEST_XML(xpath_xalan_conditional, "<letters>b</letters>") +{ +	xml_node c; + +	CHECK_XPATH_BOOLEAN(c, STR("(round(3.7) > 3)"), true); +	CHECK_XPATH_BOOLEAN(c, STR("2 > 1"), true); +	CHECK_XPATH_BOOLEAN(c, STR("9 mod 3 = 0"), true); +	CHECK_XPATH_BOOLEAN(c, STR("'a'='a'"), true); +	CHECK_XPATH_BOOLEAN(c, STR("2+2=4"), true); + +	xml_node b = doc.child(STR("letters")).first_child(); + +	CHECK_XPATH_BOOLEAN(b, STR(".='b'"), true); +	CHECK_XPATH_BOOLEAN(b, STR("name(..)='letters'"), true); +} + +TEST_XML(xpath_xalan_math_1, "<a>3</a>") +{ +	xml_node c; + +	CHECK_XPATH_NUMBER(c, STR("number('1')"), 1); +	CHECK_XPATH_NUMBER(c, STR("floor(0.0)"), 0); +	CHECK_XPATH_NUMBER(c, STR("ceiling(0.0)"), 0); +	CHECK_XPATH_NUMBER(c, STR("round(0.0)"), 0); +	CHECK_XPATH_NUMBER(c, STR("2*3"), 6); +	CHECK_XPATH_NUMBER(c, STR("3+6"), 9); +	CHECK_XPATH_NUMBER(c, STR("3-1"), 2); +	CHECK_XPATH_NUMBER_NAN(doc, STR("a-1")); // a-1 is a name test, not arithmetic expression +	CHECK_XPATH_NUMBER(doc, STR("a -1"), 2); +	CHECK_XPATH_NUMBER(c, STR("6 div 2"), 3); +	CHECK_XPATH_NUMBER(c, STR("5 mod 2"), 1); +	CHECK_XPATH_NUMBER_NAN(c, STR("number(n)")); +	CHECK_XPATH_NUMBER(c, STR("number(2)"), 2); +	CHECK_XPATH_NUMBER(c, STR("number('3')"), 3); +	CHECK_XPATH_NUMBER_NAN(c, STR("number('')")); +	CHECK_XPATH_NUMBER_NAN(c, STR("number('abc')")); +	CHECK_XPATH_BOOLEAN(c, STR("number(string(1.0))=1"), true); +	CHECK_XPATH_BOOLEAN(c, STR("number(true())=1"), true); +	CHECK_XPATH_BOOLEAN(c, STR("number(false())=0"), true); + +#ifndef MSVC6_NAN_BUG +	CHECK_XPATH_BOOLEAN(c, STR("number('xxx')=number('xxx')"), false); +	CHECK_XPATH_BOOLEAN(c, STR("number('xxx')=0"), false); +#endif + +	CHECK_XPATH_NUMBER(doc, STR("floor(a)"), 3); +	CHECK_XPATH_NUMBER(c, STR("floor(1.9)"), 1); +	CHECK_XPATH_NUMBER(c, STR("floor(2.999999)"), 2); +	CHECK_XPATH_NUMBER(c, STR("floor(-1.5)"), -2); +	CHECK_XPATH_BOOLEAN(c, STR("floor(1)=1"), true); +	CHECK_XPATH_BOOLEAN(c, STR("floor(1.9)=1"), true); +	CHECK_XPATH_BOOLEAN(c, STR("floor(-1.5)=-2"), true); +	CHECK_XPATH_NUMBER(doc, STR("ceiling(a)"), 3); +	CHECK_XPATH_NUMBER(c, STR("ceiling(1.54)"), 2); +	CHECK_XPATH_NUMBER(c, STR("ceiling(2.999999)"), 3); +	CHECK_XPATH_NUMBER(c, STR("ceiling(3.000001)"), 4); +	CHECK_XPATH_BOOLEAN(c, STR("ceiling(1)=1"), true); +	CHECK_XPATH_BOOLEAN(c, STR("ceiling(1.1)=2"), true); +	CHECK_XPATH_BOOLEAN(c, STR("ceiling(-1.5)=-1"), true); +} + +TEST_XML(xpath_xalan_math_2, "<a>3</a>") +{ +	xml_node c; + +	CHECK_XPATH_NUMBER(doc, STR("round(a)"), 3); +	CHECK_XPATH_NUMBER(c, STR("round(1.24)"), 1); +	CHECK_XPATH_NUMBER(c, STR("round(2.999999)"), 3); +	CHECK_XPATH_NUMBER(c, STR("round(3.000001)"), 3); +	CHECK_XPATH_NUMBER(c, STR("round(1.1)"), 1); +	CHECK_XPATH_NUMBER(c, STR("round(-1.1)"), -1); +	CHECK_XPATH_NUMBER(c, STR("round(1.9)"), 2); +	CHECK_XPATH_NUMBER(c, STR("round(-1.9)"), -2); +	CHECK_XPATH_NUMBER(c, STR("round(1.5)"), 2); +	CHECK_XPATH_NUMBER(c, STR("round(-1.5)"), -1); +	CHECK_XPATH_NUMBER(c, STR("round(1.4999999)"), 1); +	CHECK_XPATH_NUMBER(c, STR("round(-1.4999999)"), -1); +	CHECK_XPATH_NUMBER(c, STR("round(1.5000001)"), 2); +	CHECK_XPATH_NUMBER(c, STR("round(-1.5000001)"), -2); +} + +TEST_XML(xpath_xalan_math_3, "<doc><n v='1'/><n>2</n><n v='3'/><n>4</n><n v='5'>5</n><e>17</e><e>-5</e><e>8</e><e>-37</e></doc>") +{ +	CHECK_XPATH_NUMBER(doc, STR("sum(doc/x)"), 0); +	CHECK_XPATH_NUMBER_NAN(doc, STR("sum(doc/n)")); +	CHECK_XPATH_NUMBER(doc, STR("sum(doc/n[text()])"), 11); +	CHECK_XPATH_NUMBER(doc, STR("sum(doc/n/@v)"), 9); +	CHECK_XPATH_NUMBER(doc, STR("sum(doc/e)"), -17); +} + +TEST_XML(xpath_xalan_math_4, "<doc><n1 a='1'>2</n1><n2 a='2'>3</n2><n1-n2>123</n1-n2><n-1>72</n-1><n-2>12</n-2><div a='2'>5</div><mod a='5'>2</mod></doc>") +{ +	xml_node c = doc.child(STR("doc")); + +	CHECK_XPATH_NUMBER(c, STR("n1*n2"), 6); +	CHECK_XPATH_NUMBER(c, STR("n1/@a*n2/@a"), 2); +	CHECK_XPATH_NUMBER(c, STR("(n1/@a)*(n2/@a)"), 2); +	CHECK_XPATH_NUMBER(c, STR("n1+n2"), 5); +	CHECK_XPATH_NUMBER(c, STR("n1/@a+n2/@a"), 3); +	CHECK_XPATH_NUMBER(c, STR("(n1/@a)+(n2/@a)"), 3); +	CHECK_XPATH_NUMBER(c, STR("1-2"), -1); +	CHECK_XPATH_NUMBER(c, STR("n1 - n2"), -1); +	CHECK_XPATH_NUMBER(c, STR("n1-n2"), 123); +	CHECK_XPATH_NUMBER(c, STR("n-1 - n-2"), 60); +	CHECK_XPATH_NUMBER(c, STR("n-1 -n-2"), 60); +	CHECK_XPATH_NUMBER(c, STR("7+-3"), 4); +	CHECK_XPATH_NUMBER(c, STR("n-1+-n-2"), 60); +	CHECK_XPATH_NUMBER(c, STR("7 - -3"), 10); +	CHECK_XPATH_NUMBER(c, STR("n-1 - -n-2"), 84); +	CHECK_XPATH_NUMBER(c, STR("-7 --3"), -4); +	CHECK_XPATH_NUMBER(c, STR("-n-1 --n-2"), -60); + +	CHECK_XPATH_FAIL(STR("+7")); +	CHECK_XPATH_FAIL(STR("7++3")); +	CHECK_XPATH_FAIL(STR("7-+3")); + +	CHECK_XPATH_NUMBER(c, STR("6 div -2"), -3); +	CHECK_XPATH_NUMBER(c, STR("n1 div n2"), 2.0 / 3.0); +	CHECK_XPATH_NUMBER(c, STR("div div mod"), 2.5); +	CHECK_XPATH_NUMBER(c, STR("div/@a div mod/@a"), 0.4); + +	CHECK_XPATH_BOOLEAN(c, STR("1 div -0 = 2 div -0"), true); +	CHECK_XPATH_BOOLEAN(c, STR("1 div -0 = 1 div 0"), false); +	CHECK_XPATH_BOOLEAN(c, STR("1 div -0 = -1 div 0"), true); + +#ifndef MSVC6_NAN_BUG +	CHECK_XPATH_BOOLEAN(c, STR("0 div 0 >= 0"), false); +	CHECK_XPATH_BOOLEAN(c, STR("0 div 0 < 0"), false); +#endif + +	CHECK_XPATH_NUMBER(c, STR("n1 mod n2"), 2); +	CHECK_XPATH_NUMBER(c, STR("div mod mod"), 1); +	CHECK_XPATH_NUMBER(c, STR("div/@a mod mod/@a"), 2); + +	CHECK_XPATH_BOOLEAN(c, STR("(5 mod 2 = 1) and (5 mod -2 = 1) and (-5 mod 2 = -1) and (-5 mod -2 = -1)"), true); +} + +TEST(xpath_xalan_math_5) +{ +	xml_node c; + +	CHECK_XPATH_NUMBER(c, STR("(((((('3'+5)*(3)+((('2')+2)*('1' - 6)))-('4' - '2'))+(-(4-6)))))"), 4); +	CHECK_XPATH_NUMBER(c, STR("1*1*2*2*2*3*3*1*1*1*0.5*0.5"), 18); +	CHECK_XPATH_NUMBER(c, STR("1440 div 2 div 2 div 6"), 60); +	CHECK_XPATH_NUMBER(c, STR("1440 div 2 div 2 div 6 div 10"), 6); +	CHECK_XPATH_NUMBER(c, STR("1440 div 2 div 2 div 6 div 10 div 3"), 2); +	CHECK_XPATH_NUMBER(c, STR("(1*2*3*4*5*6)div 2 div 6 div 10 div 3"), 2); +	CHECK_XPATH_NUMBER_NAN(c, STR("(2 + number('xxx'))")); +	CHECK_XPATH_NUMBER_NAN(c, STR("2 * -number('xxx')")); +	CHECK_XPATH_NUMBER_NAN(c, STR("2 - number('xxx')")); +	CHECK_XPATH_NUMBER_NAN(c, STR("number('xxx') - 3")); +	CHECK_XPATH_NUMBER_NAN(c, STR("2 div number('xxx')")); +	CHECK_XPATH_NUMBER_NAN(c, STR("number('xxx') div 3")); + +#ifndef __BORLANDC__ // BCC fmod does not propagate NaN correctly +	CHECK_XPATH_NUMBER_NAN(c, STR("2 mod number('xxx')")); +	CHECK_XPATH_NUMBER_NAN(c, STR("number('xxx') mod 3")); +#endif + +	CHECK_XPATH_NUMBER_NAN(c, STR("floor(number('xxx'))")); +	CHECK_XPATH_NUMBER_NAN(c, STR("ceiling(number('xxx'))")); +	CHECK_XPATH_NUMBER_NAN(c, STR("round(number('xxx'))")); +	CHECK_XPATH_NUMBER(c, STR("10+5+25+20+15+50+35+40"), 200); +	CHECK_XPATH_NUMBER(c, STR("100-9-7-4-17-18-5"), 40); +	CHECK_XPATH_NUMBER(c, STR("3*2+5*4-4*2-1"), 17); +    CHECK_XPATH_NUMBER(c, STR("6*5-8*2+5*2"), 24); +    CHECK_XPATH_NUMBER(c, STR("10*5-4*2+6*1 -3*3"), 39); + +    CHECK_XPATH_NUMBER(c, STR("(24 div 3 +2) div (40 div 8 -3)"), 5); +    CHECK_XPATH_NUMBER(c, STR("80 div 2 + 12 div 2 - 4 div 2"), 44); +    CHECK_XPATH_NUMBER(c, STR("70 div 10 - 18 div 6 + 10 div 2"), 9); + +    CHECK_XPATH_NUMBER(c, STR("48 mod 17 - 2 mod 9 + 13 mod 5"), 15); +    CHECK_XPATH_NUMBER(c, STR("56 mod round(5*2+1.444) - 6 mod 4 + 7 mod 4"), 2); +    CHECK_XPATH_NUMBER(c, STR("(77 mod 10 + 5 mod 8) mod 10"), 2); +} + +TEST_XML(xpath_xalan_math_6, "<doc><n1>3</n1><n2>7</n2><n3>x</n3></doc>") +{ +	xml_node c = doc.child(STR("doc")); + +	CHECK_XPATH_NUMBER(c, STR("-(n1|n2)"), -3); +	CHECK_XPATH_NUMBER(c, STR("-(n2|n1)"), -3); +	CHECK_XPATH_BOOLEAN(c, STR("contains(number(n1), 'NaN')"), false); +	CHECK_XPATH_BOOLEAN(c, STR("contains(number(n3), 'NaN')"), true); +} + +TEST_XML(xpath_xalan_math_7, "<doc><n1>3</n1><n2>7</n2><n3>x</n3></doc>") +{ +	xml_node c = doc.child(STR("doc")); + +	CHECK_XPATH_NUMBER(c, STR("-(n1|n2)"), -3); +	CHECK_XPATH_NUMBER(c, STR("-(n2|n1)"), -3); +	CHECK_XPATH_BOOLEAN(c, STR("contains(number(n1), 'NaN')"), false); +	CHECK_XPATH_BOOLEAN(c, STR("contains(number(n3), 'NaN')"), true); +} + +TEST_XML(xpath_xalan_math_8, "<k>0.0004</k>") +{ +	CHECK_XPATH_NUMBER(doc, STR("number(1.75)"), 1.75); +	CHECK_XPATH_NUMBER(doc, STR("number(7 div 4)"), 1.75); +	CHECK_XPATH_BOOLEAN(doc, STR("(number(1.75) = (7 div 4))"), true); +	CHECK_XPATH_NUMBER(doc, STR("number(0.109375 * 16)"), 1.75); +	CHECK_XPATH_BOOLEAN(doc, STR("(number(1.75) = (0.109375 * 16))"), true); +	CHECK_XPATH_NUMBER(doc, STR("number(k)"), 0.0004); +	CHECK_XPATH_NUMBER(doc, STR("number(4 div 10000)"), 0.0004); +	CHECK_XPATH_BOOLEAN(doc, STR("(number(k) = (4 div 10000))"), true); +	CHECK_XPATH_NUMBER(doc, STR("number(0.0001 * 4)"), 0.0004); +	CHECK_XPATH_BOOLEAN(doc, STR("(number(k) = (0.0001 * 4))"), true); +} + +TEST(xpath_xalan_math_9) +{ +	xml_node c; + +	CHECK_XPATH_STRING(c, STR("string(number('0.0'))"), STR("0")); +	CHECK_XPATH_STRING(c, STR("string(-1 * number('0.0'))"), STR("0")); + +	CHECK_XPATH_STRING(c, STR("string(number('0.4'))"), STR("0.4")); +	CHECK_XPATH_STRING(c, STR("string(-1 * number('0.4'))"), STR("-0.4")); + +	CHECK_XPATH_STRING(c, STR("string(number('4.0'))"), STR("4")); +	CHECK_XPATH_STRING(c, STR("string(-1 * number('4.0'))"), STR("-4")); + +	CHECK_XPATH_STRING(c, STR("string(number('0.04'))"), STR("0.04")); +	CHECK_XPATH_STRING(c, STR("string(-1 * number('0.04'))"), STR("-0.04")); + +	CHECK_XPATH_STRING(c, STR("string(number('0.004'))"), STR("0.004")); +	CHECK_XPATH_STRING(c, STR("string(-1 * number('0.004'))"), STR("-0.004")); + +	CHECK_XPATH_STRING(c, STR("string(number('0.0004'))"), STR("0.0004")); +	CHECK_XPATH_STRING(c, STR("string(-1 * number('0.0004'))"), STR("-0.0004")); + +	CHECK_XPATH_STRING(c, STR("string(number('0.0000000000001'))"), STR("0.0000000000001")); +	CHECK_XPATH_STRING(c, STR("string(-1 * number('0.0000000000001'))"), STR("-0.0000000000001")); + +	CHECK_XPATH_STRING(c, STR("string(number('0.0000000000000000000000000001'))"), STR("0.0000000000000000000000000001")); +	CHECK_XPATH_STRING(c, STR("string(-1 * number('0.0000000000000000000000000001'))"), STR("-0.0000000000000000000000000001")); + +	CHECK_XPATH_STRING(c, STR("string(number('0.0000000000001000000000000001'))"), STR("0.0000000000001000000000000001")); +	CHECK_XPATH_STRING(c, STR("string(-1 * number('0.0000000000001000000000000001'))"), STR("-0.0000000000001000000000000001")); + +	CHECK_XPATH_STRING(c, STR("string(number('0.0012'))"), STR("0.0012")); +	CHECK_XPATH_STRING(c, STR("string(-1 * number('0.0012'))"), STR("-0.0012")); + +	CHECK_XPATH_STRING(c, STR("string(number('0.012'))"), STR("0.012")); +	CHECK_XPATH_STRING(c, STR("string(-1 * number('0.012'))"), STR("-0.012")); +} + +#endif diff --git a/tests/test_xpath_xalan_2.cpp b/tests/test_xpath_xalan_2.cpp index abc6a1c..aa8ae17 100644 --- a/tests/test_xpath_xalan_2.cpp +++ b/tests/test_xpath_xalan_2.cpp @@ -1,399 +1,399 @@ -#ifndef PUGIXML_NO_XPATH
 -
 -#define _CRT_SECURE_NO_WARNINGS
 -
 -#include "common.hpp"
 -
 -#include <string>
 -
 -TEST_XML(xpath_xalan_string_1, "<doc a='test'>ENCYCLOPEDIA</doc>")
 -{
 -	xml_node c;
 -
 -	CHECK_XPATH_NUMBER(c, STR("string-length('This is a test')"), 14);
 -	CHECK_XPATH_BOOLEAN(c, STR("starts-with('ENCYCLOPEDIA', 'ENCY')"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("contains('ENCYCLOPEDIA', 'CYCL')"), true);
 -	CHECK_XPATH_STRING(c, STR("substring-before('1999/04/01', '/')"), STR("1999"));
 -	CHECK_XPATH_STRING(c, STR("substring-after('1999/04/01', '/')"), STR("04/01"));
 -	CHECK_XPATH_STRING(c, STR("normalize-space('\t\n\r\n    ab\n     cd\t\n\r\n    ef\t\n\r ')"), STR("ab cd ef"));
 -	CHECK_XPATH_STRING(c, STR("translate(\"bar\",\"abc\",\"ABC\")"), STR("BAr"));
 -	CHECK_XPATH_STRING(c, STR("concat(\"x\",\"yz\")"), STR("xyz"));
 -	CHECK_XPATH_STRING(c, STR("substring('1999/04/01', 1, 4)"), STR("1999"));
 -	CHECK_XPATH_STRING(c, STR("substring('12345', 1.5, 2.6)"), STR("234"));
 -	CHECK_XPATH_STRING(c, STR("substring('12345', 0, 3)"), STR("12"));
 -	CHECK_XPATH_STRING(c, STR("substring('12345', 0 div 0, 3)"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("substring('12345', 1, 0 div 0)"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("substring('12345', -42, 1 div 0)"), STR("12345"));
 -	CHECK_XPATH_STRING(c, STR("substring('12345', -1 div 0, 1 div 0)"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("substring(foo, 12, 3)"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("string(foo)"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("string(0)"), STR("0"));
 -	CHECK_XPATH_STRING(c, STR("string(2)"), STR("2"));
 -	CHECK_XPATH_STRING(c, STR("string('test')"), STR("test"));
 -	CHECK_XPATH_STRING(c, STR("string('')"), STR(""));
 -	CHECK_XPATH_BOOLEAN(c, STR("starts-with('ENCYCLOPEDIA', 'EN')"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("starts-with('ENCYCLOPEDIA', 'en')"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("starts-with('ab', 'abc')"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("starts-with('abc', 'bc')"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("starts-with('abc', '')"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("starts-with('', '')"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("starts-with('', 'abc')"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("starts-with('true()', 'tr')"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("starts-with(foo, 'EN')"), false);
 -	CHECK_XPATH_BOOLEAN(doc, STR("starts-with(doc, 'EN')"), true);
 -	CHECK_XPATH_BOOLEAN(doc, STR("starts-with(doc/@a, 'EN')"), false);
 -	CHECK_XPATH_BOOLEAN(doc, STR("starts-with(doc/@a, 'te')"), true);
 -}
 -
 -TEST_XML_FLAGS(xpath_xalan_string_2, "<doc>\n  <av>\n    <a>\n      <b>b</b>\n      <c>c</c>\n      <d>d</d>\n      <e>e</e>\n    </a>\n    <v>\n      <w>w</w>\n      <x>x</x>\n      <y>y</y>\n      <z>z</z>\n    </v>\n  </av>\n</doc>", parse_default | parse_ws_pcdata)
 -{
 -	CHECK_XPATH_STRING(doc, STR("string(doc/av//*)"), STR("\n      b\n      c\n      d\n      e\n    "));
 -	CHECK_XPATH_STRING(doc, STR("normalize-space(string(doc/av//*))"), STR("b c d e"));
 -	CHECK_XPATH_STRING(doc, STR("normalize-space('This       is       a       test')"), STR("This is a test"));
 -}
 -
 -TEST_XML(xpath_xalan_string_3, "<doc a='test'>ENCYCLOPEDIA</doc>")
 -{
 -	xml_node c;
 -
 -	CHECK_XPATH_BOOLEAN(c, STR("contains('ENCYCLOPEDIA', 'TEST')"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("contains('ENCYCLOPEDIA', 'CYCL')"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("contains('ENCYCLOPEDIA', 'cycl')"), false);
 -	CHECK_XPATH_BOOLEAN(doc, STR("contains(concat(.,'BC'),concat('A','B','C'))"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("contains('ab', 'abc')"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("contains('abc', 'bc')"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("contains('abc', 'bcd')"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("contains('abc', '')"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("contains('', '')"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("contains('', 'abc')"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("contains('true()', 'e')"), true);
 -	CHECK_XPATH_BOOLEAN(doc, STR("contains(., 'CYCL')"), true);
 -	CHECK_XPATH_BOOLEAN(doc, STR("contains(., 'TEST')"), false);
 -	CHECK_XPATH_BOOLEAN(doc, STR("contains(doc/@a, 'es')"), true);
 -	CHECK_XPATH_BOOLEAN(doc, STR("contains(doc/@a, 'T')"), false);
 -	CHECK_XPATH_STRING(c, STR("substring-before('ENCYCLOPEDIA', '/')"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("substring-before('ENCYCLOPEDIA', 'C')"), STR("EN"));
 -	CHECK_XPATH_STRING(c, STR("substring-before('ENCYCLOPEDIA', 'c')"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("substring-before('ENCYCLOPEDIA', '')"), STR(""));
 -	CHECK_XPATH_STRING(doc, STR("substring-before(., '/')"), STR(""));
 -	CHECK_XPATH_STRING(doc, STR("substring-before(., 'C')"), STR("EN"));
 -	CHECK_XPATH_STRING(doc, STR("substring-before(foo, '')"), STR(""));
 -	CHECK_XPATH_STRING(doc, STR("substring-before(doc/@a, '/')"), STR(""));
 -	CHECK_XPATH_STRING(doc, STR("substring-before(doc/@a, 'e')"), STR("t"));
 -	CHECK_XPATH_STRING(doc, STR("substring-before(doc/@a, 't')"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("substring-after('ENCYCLOPEDIA', '/')"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("substring-after('ENCYCLOPEDIA', 'C')"), STR("YCLOPEDIA"));
 -	CHECK_XPATH_STRING(c, STR("substring-after('ENCYCLOPEDIA', 'c')"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("substring-after('ENCYCLOPEDIA', '')"), STR("ENCYCLOPEDIA"));
 -	CHECK_XPATH_STRING(doc, STR("substring-after(., '/')"), STR(""));
 -	CHECK_XPATH_STRING(doc, STR("substring-after(., 'C')"), STR("YCLOPEDIA"));
 -	CHECK_XPATH_STRING(doc, STR("substring-after(foo, '')"), STR(""));
 -	CHECK_XPATH_STRING(doc, STR("substring-after(doc/@a, '/')"), STR(""));
 -	CHECK_XPATH_STRING(doc, STR("substring-after(doc/@a, 'e')"), STR("st"));
 -	CHECK_XPATH_STRING(doc, STR("substring-after(doc/@a, 't')"), STR("est"));
 -	CHECK_XPATH_STRING(doc, STR("substring-after(doc/@a, 'st')"), STR(""));
 -}
 -
 -TEST_XML(xpath_xalan_string_4, "<doc><a>a</a><b>b</b><c>c</c><d>d</d><e>ef</e><f attr='whatsup'>what's up</f></doc><cd><![CDATA[qua'lit\"y]]></cd>")
 -{
 -	xml_node c;
 -
 -	CHECK_XPATH_STRING(c, STR("translate('BAR','abc','ABC')"), STR("BAR"));
 -	CHECK_XPATH_STRING(c, STR("translate('bar','RAB','xyz')"), STR("bar"));
 -	CHECK_XPATH_STRING(c, STR("translate('BAR','Rab','TxX')"), STR("BAT"));
 -	CHECK_XPATH_STRING(c, STR("translate('zzaaazzz','abcz','ABC')"), STR("AAA"));
 -	CHECK_XPATH_STRING(c, STR("translate('ddaaadddd','abcd','ABCxy')"), STR("xxAAAxxxx"));
 -	CHECK_XPATH_STRING(c, STR("concat('a','b','c','d','ef')"), STR("abcdef"));
 -	CHECK_XPATH_STRING(c, STR("concat(a, b)"), STR(""));
 -	CHECK_XPATH_STRING(doc.child(STR("doc")), STR("concat(a, b)"), STR("ab"));
 -	CHECK_XPATH_STRING(doc.child(STR("doc")), STR("concat(a, b, c, d, e)"), STR("abcdef"));
 -	CHECK_XPATH_STRING(c, STR("concat('cd','34')"), STR("cd34"));
 -	CHECK_XPATH_STRING(c, STR("concat('cd',34)"), STR("cd34"));
 -	CHECK_XPATH_STRING(c, STR("concat('bc',string(23))"), STR("bc23"));
 -	CHECK_XPATH_STRING(c, STR("concat(a,34)"), STR("34"));
 -	CHECK_XPATH_STRING(doc.child(STR("doc")), STR("concat(a,34)"), STR("a34"));
 -	CHECK_XPATH_STRING(c, STR("concat(false(),'ly')"), STR("falsely"));
 -	CHECK_XPATH_FAIL(STR("concat(/*)"));
 -	CHECK_XPATH_STRING(doc.child(STR("doc")), STR("concat(/*, '')"), STR("abcdefwhat's up"));
 -	CHECK_XPATH_STRING(doc.child(STR("doc")), STR("concat(/*, /*[@attr='whatsup'])"), STR("abcdefwhat's up"));
 -	CHECK_XPATH_STRING(doc.child(STR("doc")), STR("concat(/*, //*[@attr='whatsup'])"), STR("abcdefwhat's upwhat's up"));
 -	CHECK_XPATH_STRING(c, STR("substring('ENCYCLOPEDIA', 8, 3)"), STR("PED"));
 -	CHECK_XPATH_STRING(c, STR("substring('ENCYCLOPEDIA', 8)"), STR("PEDIA"));
 -	CHECK_XPATH_STRING(c, STR("substring('abcdefghijk',0 div 0, 5)"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("substring('abcdefghijk',4, 6)"), STR("defghi"));
 -	CHECK_XPATH_STRING(c, STR("substring('1999/04/01', 1, 0)"), STR(""));
 -	CHECK_XPATH_STRING(c, STR("translate(normalize-space('     bar       fly        '), ' ', '_')"), STR("bar_fly"));
 -	CHECK_XPATH_STRING(c, STR("translate('barter','abe','bao')"), STR("abrtor"));
 -	CHECK_XPATH_STRING(c, STR("translate('barbarity', 'aeiouy', '******')"), STR("b*rb*r*t*"));
 -	CHECK_XPATH_STRING(doc, STR("translate(cd, concat(\"aqu'\", '\"eos'), 'AQU-+EOS')"), STR("QUA-lit+y"));
 -	CHECK_XPATH_STRING(c, STR("translate('quan+ti-ty', 'AQU-+EOS', concat(\"aqu'\", '\"eos'))"), STR("quan\"ti'ty"));
 -}
 -
 -static std::basic_string<char_t> number_to_string(int number)
 -{
 -	std::basic_string<char_t> result;
 -
 -	while (number)
 -	{
 -		result = static_cast<char_t>('0' + number % 10) + result;
 -		number /= 10;
 -	}
 -
 -	return result;
 -}
 -
 -TEST(xpath_xalan_string_5)
 -{
 -	std::basic_string<char_t> query = STR("concat(");
 -
 -	for (int i = 1; i < 1000; ++i)
 -	{
 -		query += STR("concat('t',");
 -		query += number_to_string(i);
 -		query += STR("), ");
 -	}
 -
 -	query += STR("'')");
 -
 -	std::basic_string<char_t> expected;
 -
 -	for (int j = 1; j < 1000; ++j)
 -	{
 -		expected += STR("t");
 -		expected += number_to_string(j);
 -	}
 -
 -	CHECK_XPATH_STRING(xml_node(), query.c_str(), expected.c_str());
 -}
 -
 -TEST(xpath_xalan_string_6)
 -{
 -	xml_node c;
 -
 -	CHECK_XPATH_STRING(c, STR("string(1)"), STR("1"));
 -	CHECK_XPATH_STRING(c, STR("string(12)"), STR("12"));
 -	CHECK_XPATH_STRING(c, STR("string(123)"), STR("123"));
 -	CHECK_XPATH_STRING(c, STR("string(1234)"), STR("1234"));
 -	CHECK_XPATH_STRING(c, STR("string(12345)"), STR("12345"));
 -	CHECK_XPATH_STRING(c, STR("string(123456)"), STR("123456"));
 -	CHECK_XPATH_STRING(c, STR("string(1234567)"), STR("1234567"));
 -	CHECK_XPATH_STRING(c, STR("string(12345678)"), STR("12345678"));
 -	CHECK_XPATH_STRING(c, STR("string(123456789)"), STR("123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(1234567890)"), STR("1234567890"));
 -	CHECK_XPATH_STRING(c, STR("string(12345678901)"), STR("12345678901"));
 -	CHECK_XPATH_STRING(c, STR("string(123456789012)"), STR("123456789012"));
 -	CHECK_XPATH_STRING(c, STR("string(1234567890123)"), STR("1234567890123"));
 -	CHECK_XPATH_STRING(c, STR("string(12345678901234)"), STR("12345678901234"));
 -	CHECK_XPATH_STRING(c, STR("string(123456789012345)"), STR("123456789012345"));
 -	CHECK_XPATH_STRING(c, STR("string(1234567890123456)"), STR("1234567890123456"));
 -	CHECK_XPATH_STRING(c, STR("string(-1)"), STR("-1"));
 -	CHECK_XPATH_STRING(c, STR("string(-12)"), STR("-12"));
 -	CHECK_XPATH_STRING(c, STR("string(-123)"), STR("-123"));
 -	CHECK_XPATH_STRING(c, STR("string(-1234)"), STR("-1234"));
 -	CHECK_XPATH_STRING(c, STR("string(-12345)"), STR("-12345"));
 -	CHECK_XPATH_STRING(c, STR("string(-123456)"), STR("-123456"));
 -	CHECK_XPATH_STRING(c, STR("string(-1234567)"), STR("-1234567"));
 -	CHECK_XPATH_STRING(c, STR("string(-12345678)"), STR("-12345678"));
 -	CHECK_XPATH_STRING(c, STR("string(-123456789)"), STR("-123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-1234567890)"), STR("-1234567890"));
 -	CHECK_XPATH_STRING(c, STR("string(-12345678901)"), STR("-12345678901"));
 -	CHECK_XPATH_STRING(c, STR("string(-123456789012)"), STR("-123456789012"));
 -	CHECK_XPATH_STRING(c, STR("string(-1234567890123)"), STR("-1234567890123"));
 -	CHECK_XPATH_STRING(c, STR("string(-12345678901234)"), STR("-12345678901234"));
 -	CHECK_XPATH_STRING(c, STR("string(-123456789012345)"), STR("-123456789012345"));
 -	CHECK_XPATH_STRING(c, STR("string(-1234567890123456)"), STR("-1234567890123456"));
 -}
 -
 -#if 0 // $ this test requires round-to-nearest behavior in string->number conversion during parsing; atof gives us truncation
 -TEST(xpath_xalan_string_6_rounding)
 -{
 -	xml_node c;
 -
 -	CHECK_XPATH_STRING(c, STR("string(12345678901234567)"), STR("12345678901234568"));
 -	CHECK_XPATH_STRING(c, STR("string(123456789012345678)"), STR("123456789012345680"));
 -	CHECK_XPATH_STRING(c, STR("string(-12345678901234567)"), STR("-12345678901234568"));
 -	CHECK_XPATH_STRING(c, STR("string(-123456789012345678)"), STR("-123456789012345680"));
 -}
 -#endif
 -
 -TEST(xpath_xalan_string_7)
 -{
 -	xml_node c;
 -
 -	CHECK_XPATH_STRING(c, STR("string(.1)"), STR("0.1"));
 -	CHECK_XPATH_STRING(c, STR("string(.01)"), STR("0.01"));
 -	CHECK_XPATH_STRING(c, STR("string(.012)"), STR("0.012"));
 -	CHECK_XPATH_STRING(c, STR("string(.0123)"), STR("0.0123"));
 -	CHECK_XPATH_STRING(c, STR("string(.01234)"), STR("0.01234"));
 -	CHECK_XPATH_STRING(c, STR("string(.012345)"), STR("0.012345"));
 -	CHECK_XPATH_STRING(c, STR("string(.0123456)"), STR("0.0123456"));
 -	CHECK_XPATH_STRING(c, STR("string(.01234567)"), STR("0.01234567"));
 -	CHECK_XPATH_STRING(c, STR("string(.012345678)"), STR("0.012345678"));
 -	CHECK_XPATH_STRING(c, STR("string(.0123456789)"), STR("0.0123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.10123456789)"), STR("0.10123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.101234567892)"), STR("0.101234567892"));
 -	CHECK_XPATH_STRING(c, STR("string(.1012345678923)"), STR("0.1012345678923"));
 -	CHECK_XPATH_STRING(c, STR("string(.10123456789234)"), STR("0.10123456789234"));
 -	CHECK_XPATH_STRING(c, STR("string(.101234567892345)"), STR("0.101234567892345"));
 -	CHECK_XPATH_STRING(c, STR("string(.1012345678923456)"), STR("0.1012345678923456"));
 -	CHECK_XPATH_STRING(c, STR("string(-.1)"), STR("-0.1"));
 -	CHECK_XPATH_STRING(c, STR("string(-.01)"), STR("-0.01"));
 -	CHECK_XPATH_STRING(c, STR("string(-.012)"), STR("-0.012"));
 -	CHECK_XPATH_STRING(c, STR("string(-.0123)"), STR("-0.0123"));
 -	CHECK_XPATH_STRING(c, STR("string(-.01234)"), STR("-0.01234"));
 -	CHECK_XPATH_STRING(c, STR("string(-.012345)"), STR("-0.012345"));
 -	CHECK_XPATH_STRING(c, STR("string(-.0123456)"), STR("-0.0123456"));
 -	CHECK_XPATH_STRING(c, STR("string(-.01234567)"), STR("-0.01234567"));
 -	CHECK_XPATH_STRING(c, STR("string(-.012345678)"), STR("-0.012345678"));
 -	CHECK_XPATH_STRING(c, STR("string(-.0123456789)"), STR("-0.0123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.10123456789)"), STR("-0.10123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.101234567892)"), STR("-0.101234567892"));
 -	CHECK_XPATH_STRING(c, STR("string(-.1012345678923)"), STR("-0.1012345678923"));
 -	CHECK_XPATH_STRING(c, STR("string(-.10123456789234)"), STR("-0.10123456789234"));
 -	CHECK_XPATH_STRING(c, STR("string(-.101234567892345)"), STR("-0.101234567892345"));
 -	CHECK_XPATH_STRING(c, STR("string(-.1012345678923456)"), STR("-0.1012345678923456"));
 -}
 -
 -#if 0 // $ this test requires 16 decimal digits of mantissa in number->string conversion; we have 15 since only 15 is guaranteed, and 16 introduces 'garbage' digits in common cases like 0.4
 -TEST(xpath_xalan_string_7_precision)
 -{
 -	xml_node c;
 -
 -	CHECK_XPATH_STRING(c, STR("string(.10123456789234567)"), STR("0.10123456789234567"));
 -	CHECK_XPATH_STRING(c, STR("string(.101234567892345678)"), STR("0.10123456789234568"));
 -	CHECK_XPATH_STRING(c, STR("string(.1012345678923456789)"), STR("0.10123456789234568"));
 -	CHECK_XPATH_STRING(c, STR("string(.10123456789234567893)"), STR("0.10123456789234568"));
 -	CHECK_XPATH_STRING(c, STR("string(-.10123456789234567)"), STR("-0.10123456789234567"));
 -	CHECK_XPATH_STRING(c, STR("string(-.101234567892345678)"), STR("-0.10123456789234568"));
 -	CHECK_XPATH_STRING(c, STR("string(-.1012345678923456789)"), STR("-0.10123456789234568"));
 -	CHECK_XPATH_STRING(c, STR("string(-.10123456789234567893)"), STR("-0.10123456789234568"));
 -}
 -#endif
 -
 -TEST(xpath_xalan_string_8)
 -{
 -	xml_node c;
 -
 -	// $ originally all last digits were 5's; a fully compliant implementation should correctly convert those as well,
 -	// however some of these failed because of atof truncation
 -	CHECK_XPATH_STRING(c, STR("string(9.87654321012344)"), STR("9.87654321012344"));
 -	CHECK_XPATH_STRING(c, STR("string(98.7654321012345)"), STR("98.7654321012345"));
 -	CHECK_XPATH_STRING(c, STR("string(987.654321012345)"), STR("987.654321012345"));
 -	CHECK_XPATH_STRING(c, STR("string(9876.54321012344)"), STR("9876.54321012344"));
 -	CHECK_XPATH_STRING(c, STR("string(98765.4321012345)"), STR("98765.4321012345"));
 -	CHECK_XPATH_STRING(c, STR("string(987654.321012345)"), STR("987654.321012345"));
 -	CHECK_XPATH_STRING(c, STR("string(9876543.21012345)"), STR("9876543.21012345"));
 -	CHECK_XPATH_STRING(c, STR("string(98765432.1012345)"), STR("98765432.1012345"));
 -	CHECK_XPATH_STRING(c, STR("string(987654321.012345)"), STR("987654321.012345"));
 -	CHECK_XPATH_STRING(c, STR("string(9876543210.12344)"), STR("9876543210.12344"));
 -	CHECK_XPATH_STRING(c, STR("string(98765432101.2345)"), STR("98765432101.2345"));
 -	CHECK_XPATH_STRING(c, STR("string(987654321012.345)"), STR("987654321012.345"));
 -	CHECK_XPATH_STRING(c, STR("string(9876543210123.43)"), STR("9876543210123.43"));
 -	CHECK_XPATH_STRING(c, STR("string(98765432101234.5)"), STR("98765432101234.5"));
 -
 -	CHECK_XPATH_STRING(c, STR("string(-9.87654321012344)"), STR("-9.87654321012344"));
 -	CHECK_XPATH_STRING(c, STR("string(-98.7654321012345)"), STR("-98.7654321012345"));
 -	CHECK_XPATH_STRING(c, STR("string(-987.654321012345)"), STR("-987.654321012345"));
 -	CHECK_XPATH_STRING(c, STR("string(-9876.54321012344)"), STR("-9876.54321012344"));
 -	CHECK_XPATH_STRING(c, STR("string(-98765.4321012345)"), STR("-98765.4321012345"));
 -	CHECK_XPATH_STRING(c, STR("string(-987654.321012345)"), STR("-987654.321012345"));
 -	CHECK_XPATH_STRING(c, STR("string(-9876543.21012345)"), STR("-9876543.21012345"));
 -	CHECK_XPATH_STRING(c, STR("string(-98765432.1012345)"), STR("-98765432.1012345"));
 -	CHECK_XPATH_STRING(c, STR("string(-987654321.012345)"), STR("-987654321.012345"));
 -	CHECK_XPATH_STRING(c, STR("string(-9876543210.12344)"), STR("-9876543210.12344"));
 -	CHECK_XPATH_STRING(c, STR("string(-98765432101.2345)"), STR("-98765432101.2345"));
 -	CHECK_XPATH_STRING(c, STR("string(-987654321012.345)"), STR("-987654321012.345"));
 -	CHECK_XPATH_STRING(c, STR("string(-9876543210123.43)"), STR("-9876543210123.43"));
 -	CHECK_XPATH_STRING(c, STR("string(-98765432101234.5)"), STR("-98765432101234.5"));
 -}
 -
 -TEST(xpath_xalan_string_9)
 -{
 -	xml_node c;
 -
 -	CHECK_XPATH_STRING(c, STR("string(.123456789)"), STR("0.123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.0123456789)"), STR("0.0123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.00123456789)"), STR("0.00123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.000123456789)"), STR("0.000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.0000123456789)"), STR("0.0000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.00000123456789)"), STR("0.00000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.000000123456789)"), STR("0.000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.0000000123456789)"), STR("0.0000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.00000000123456789)"), STR("0.00000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.000000000123456789)"), STR("0.000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.0000000000123456789)"), STR("0.0000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.00000000000123456789)"), STR("0.00000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.000000000000123456789)"), STR("0.000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.0000000000000123456789)"), STR("0.0000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.00000000000000123456789)"), STR("0.00000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.000000000000000123456789)"), STR("0.000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.0000000000000000123456789)"), STR("0.0000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.00000000000000000123456789)"), STR("0.00000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.000000000000000000123456789)"), STR("0.000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.0000000000000000000123456789)"), STR("0.0000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.00000000000000000000123456789)"), STR("0.00000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.000000000000000000000123456789)"), STR("0.000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.0000000000000000000000123456789)"), STR("0.0000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.00000000000000000000000123456789)"), STR("0.00000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.000000000000000000000000123456789)"), STR("0.000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.0000000000000000000000000123456789)"), STR("0.0000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.00000000000000000000000000123456789)"), STR("0.00000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.000000000000000000000000000123456789)"), STR("0.000000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.0000000000000000000000000000123456789)"), STR("0.0000000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.00000000000000000000000000000123456789)"), STR("0.00000000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.000000000000000000000000000000123456789)"), STR("0.000000000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.0000000000000000000000000000000123456789)"), STR("0.0000000000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.00000000000000000000000000000000123456789)"), STR("0.00000000000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.000000000000000000000000000000000123456789)"), STR("0.000000000000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.0000000000000000000000000000000000123456789)"), STR("0.0000000000000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.00000000000000000000000000000000000123456789)"), STR("0.00000000000000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.000000000000000000000000000000000000123456789)"), STR("0.000000000000000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.0000000000000000000000000000000000000123456789)"), STR("0.0000000000000000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.00000000000000000000000000000000000000123456789)"), STR("0.00000000000000000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.000000000000000000000000000000000000000123456789)"), STR("0.000000000000000000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(.0000000000000000000000000000000000000000123456789)"), STR("0.0000000000000000000000000000000000000000123456789"));
 -
 -	CHECK_XPATH_STRING(c, STR("string(-.123456789)"), STR("-0.123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.0123456789)"), STR("-0.0123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.00123456789)"), STR("-0.00123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.000123456789)"), STR("-0.000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.0000123456789)"), STR("-0.0000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.00000123456789)"), STR("-0.00000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.000000123456789)"), STR("-0.000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.0000000123456789)"), STR("-0.0000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.00000000123456789)"), STR("-0.00000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.000000000123456789)"), STR("-0.000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.0000000000123456789)"), STR("-0.0000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.00000000000123456789)"), STR("-0.00000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.000000000000123456789)"), STR("-0.000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.0000000000000123456789)"), STR("-0.0000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.00000000000000123456789)"), STR("-0.00000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.000000000000000123456789)"), STR("-0.000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.0000000000000000123456789)"), STR("-0.0000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.00000000000000000123456789)"), STR("-0.00000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.000000000000000000123456789)"), STR("-0.000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.0000000000000000000123456789)"), STR("-0.0000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.00000000000000000000123456789)"), STR("-0.00000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.000000000000000000000123456789)"), STR("-0.000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.0000000000000000000000123456789)"), STR("-0.0000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.00000000000000000000000123456789)"), STR("-0.00000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.000000000000000000000000123456789)"), STR("-0.000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.0000000000000000000000000123456789)"), STR("-0.0000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.00000000000000000000000000123456789)"), STR("-0.00000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.000000000000000000000000000123456789)"), STR("-0.000000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.0000000000000000000000000000123456789)"), STR("-0.0000000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.00000000000000000000000000000123456789)"), STR("-0.00000000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.000000000000000000000000000000123456789)"), STR("-0.000000000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.0000000000000000000000000000000123456789)"), STR("-0.0000000000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.00000000000000000000000000000000123456789)"), STR("-0.00000000000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.000000000000000000000000000000000123456789)"), STR("-0.000000000000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.0000000000000000000000000000000000123456789)"), STR("-0.0000000000000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.00000000000000000000000000000000000123456789)"), STR("-0.00000000000000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.000000000000000000000000000000000000123456789)"), STR("-0.000000000000000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.0000000000000000000000000000000000000123456789)"), STR("-0.0000000000000000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.00000000000000000000000000000000000000123456789)"), STR("-0.00000000000000000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.000000000000000000000000000000000000000123456789)"), STR("-0.000000000000000000000000000000000000000123456789"));
 -	CHECK_XPATH_STRING(c, STR("string(-.0000000000000000000000000000000000000000123456789)"), STR("-0.0000000000000000000000000000000000000000123456789"));
 -}
 -
 -#endif
 +#ifndef PUGIXML_NO_XPATH + +#define _CRT_SECURE_NO_WARNINGS + +#include "common.hpp" + +#include <string> + +TEST_XML(xpath_xalan_string_1, "<doc a='test'>ENCYCLOPEDIA</doc>") +{ +	xml_node c; + +	CHECK_XPATH_NUMBER(c, STR("string-length('This is a test')"), 14); +	CHECK_XPATH_BOOLEAN(c, STR("starts-with('ENCYCLOPEDIA', 'ENCY')"), true); +	CHECK_XPATH_BOOLEAN(c, STR("contains('ENCYCLOPEDIA', 'CYCL')"), true); +	CHECK_XPATH_STRING(c, STR("substring-before('1999/04/01', '/')"), STR("1999")); +	CHECK_XPATH_STRING(c, STR("substring-after('1999/04/01', '/')"), STR("04/01")); +	CHECK_XPATH_STRING(c, STR("normalize-space('\t\n\r\n    ab\n     cd\t\n\r\n    ef\t\n\r ')"), STR("ab cd ef")); +	CHECK_XPATH_STRING(c, STR("translate(\"bar\",\"abc\",\"ABC\")"), STR("BAr")); +	CHECK_XPATH_STRING(c, STR("concat(\"x\",\"yz\")"), STR("xyz")); +	CHECK_XPATH_STRING(c, STR("substring('1999/04/01', 1, 4)"), STR("1999")); +	CHECK_XPATH_STRING(c, STR("substring('12345', 1.5, 2.6)"), STR("234")); +	CHECK_XPATH_STRING(c, STR("substring('12345', 0, 3)"), STR("12")); +	CHECK_XPATH_STRING(c, STR("substring('12345', 0 div 0, 3)"), STR("")); +	CHECK_XPATH_STRING(c, STR("substring('12345', 1, 0 div 0)"), STR("")); +	CHECK_XPATH_STRING(c, STR("substring('12345', -42, 1 div 0)"), STR("12345")); +	CHECK_XPATH_STRING(c, STR("substring('12345', -1 div 0, 1 div 0)"), STR("")); +	CHECK_XPATH_STRING(c, STR("substring(foo, 12, 3)"), STR("")); +	CHECK_XPATH_STRING(c, STR("string(foo)"), STR("")); +	CHECK_XPATH_STRING(c, STR("string(0)"), STR("0")); +	CHECK_XPATH_STRING(c, STR("string(2)"), STR("2")); +	CHECK_XPATH_STRING(c, STR("string('test')"), STR("test")); +	CHECK_XPATH_STRING(c, STR("string('')"), STR("")); +	CHECK_XPATH_BOOLEAN(c, STR("starts-with('ENCYCLOPEDIA', 'EN')"), true); +	CHECK_XPATH_BOOLEAN(c, STR("starts-with('ENCYCLOPEDIA', 'en')"), false); +	CHECK_XPATH_BOOLEAN(c, STR("starts-with('ab', 'abc')"), false); +	CHECK_XPATH_BOOLEAN(c, STR("starts-with('abc', 'bc')"), false); +	CHECK_XPATH_BOOLEAN(c, STR("starts-with('abc', '')"), true); +	CHECK_XPATH_BOOLEAN(c, STR("starts-with('', '')"), true); +	CHECK_XPATH_BOOLEAN(c, STR("starts-with('', 'abc')"), false); +	CHECK_XPATH_BOOLEAN(c, STR("starts-with('true()', 'tr')"), true); +	CHECK_XPATH_BOOLEAN(c, STR("starts-with(foo, 'EN')"), false); +	CHECK_XPATH_BOOLEAN(doc, STR("starts-with(doc, 'EN')"), true); +	CHECK_XPATH_BOOLEAN(doc, STR("starts-with(doc/@a, 'EN')"), false); +	CHECK_XPATH_BOOLEAN(doc, STR("starts-with(doc/@a, 'te')"), true); +} + +TEST_XML_FLAGS(xpath_xalan_string_2, "<doc>\n  <av>\n    <a>\n      <b>b</b>\n      <c>c</c>\n      <d>d</d>\n      <e>e</e>\n    </a>\n    <v>\n      <w>w</w>\n      <x>x</x>\n      <y>y</y>\n      <z>z</z>\n    </v>\n  </av>\n</doc>", parse_default | parse_ws_pcdata) +{ +	CHECK_XPATH_STRING(doc, STR("string(doc/av//*)"), STR("\n      b\n      c\n      d\n      e\n    ")); +	CHECK_XPATH_STRING(doc, STR("normalize-space(string(doc/av//*))"), STR("b c d e")); +	CHECK_XPATH_STRING(doc, STR("normalize-space('This       is       a       test')"), STR("This is a test")); +} + +TEST_XML(xpath_xalan_string_3, "<doc a='test'>ENCYCLOPEDIA</doc>") +{ +	xml_node c; + +	CHECK_XPATH_BOOLEAN(c, STR("contains('ENCYCLOPEDIA', 'TEST')"), false); +	CHECK_XPATH_BOOLEAN(c, STR("contains('ENCYCLOPEDIA', 'CYCL')"), true); +	CHECK_XPATH_BOOLEAN(c, STR("contains('ENCYCLOPEDIA', 'cycl')"), false); +	CHECK_XPATH_BOOLEAN(doc, STR("contains(concat(.,'BC'),concat('A','B','C'))"), true); +	CHECK_XPATH_BOOLEAN(c, STR("contains('ab', 'abc')"), false); +	CHECK_XPATH_BOOLEAN(c, STR("contains('abc', 'bc')"), true); +	CHECK_XPATH_BOOLEAN(c, STR("contains('abc', 'bcd')"), false); +	CHECK_XPATH_BOOLEAN(c, STR("contains('abc', '')"), true); +	CHECK_XPATH_BOOLEAN(c, STR("contains('', '')"), true); +	CHECK_XPATH_BOOLEAN(c, STR("contains('', 'abc')"), false); +	CHECK_XPATH_BOOLEAN(c, STR("contains('true()', 'e')"), true); +	CHECK_XPATH_BOOLEAN(doc, STR("contains(., 'CYCL')"), true); +	CHECK_XPATH_BOOLEAN(doc, STR("contains(., 'TEST')"), false); +	CHECK_XPATH_BOOLEAN(doc, STR("contains(doc/@a, 'es')"), true); +	CHECK_XPATH_BOOLEAN(doc, STR("contains(doc/@a, 'T')"), false); +	CHECK_XPATH_STRING(c, STR("substring-before('ENCYCLOPEDIA', '/')"), STR("")); +	CHECK_XPATH_STRING(c, STR("substring-before('ENCYCLOPEDIA', 'C')"), STR("EN")); +	CHECK_XPATH_STRING(c, STR("substring-before('ENCYCLOPEDIA', 'c')"), STR("")); +	CHECK_XPATH_STRING(c, STR("substring-before('ENCYCLOPEDIA', '')"), STR("")); +	CHECK_XPATH_STRING(doc, STR("substring-before(., '/')"), STR("")); +	CHECK_XPATH_STRING(doc, STR("substring-before(., 'C')"), STR("EN")); +	CHECK_XPATH_STRING(doc, STR("substring-before(foo, '')"), STR("")); +	CHECK_XPATH_STRING(doc, STR("substring-before(doc/@a, '/')"), STR("")); +	CHECK_XPATH_STRING(doc, STR("substring-before(doc/@a, 'e')"), STR("t")); +	CHECK_XPATH_STRING(doc, STR("substring-before(doc/@a, 't')"), STR("")); +	CHECK_XPATH_STRING(c, STR("substring-after('ENCYCLOPEDIA', '/')"), STR("")); +	CHECK_XPATH_STRING(c, STR("substring-after('ENCYCLOPEDIA', 'C')"), STR("YCLOPEDIA")); +	CHECK_XPATH_STRING(c, STR("substring-after('ENCYCLOPEDIA', 'c')"), STR("")); +	CHECK_XPATH_STRING(c, STR("substring-after('ENCYCLOPEDIA', '')"), STR("ENCYCLOPEDIA")); +	CHECK_XPATH_STRING(doc, STR("substring-after(., '/')"), STR("")); +	CHECK_XPATH_STRING(doc, STR("substring-after(., 'C')"), STR("YCLOPEDIA")); +	CHECK_XPATH_STRING(doc, STR("substring-after(foo, '')"), STR("")); +	CHECK_XPATH_STRING(doc, STR("substring-after(doc/@a, '/')"), STR("")); +	CHECK_XPATH_STRING(doc, STR("substring-after(doc/@a, 'e')"), STR("st")); +	CHECK_XPATH_STRING(doc, STR("substring-after(doc/@a, 't')"), STR("est")); +	CHECK_XPATH_STRING(doc, STR("substring-after(doc/@a, 'st')"), STR("")); +} + +TEST_XML(xpath_xalan_string_4, "<doc><a>a</a><b>b</b><c>c</c><d>d</d><e>ef</e><f attr='whatsup'>what's up</f></doc><cd><![CDATA[qua'lit\"y]]></cd>") +{ +	xml_node c; + +	CHECK_XPATH_STRING(c, STR("translate('BAR','abc','ABC')"), STR("BAR")); +	CHECK_XPATH_STRING(c, STR("translate('bar','RAB','xyz')"), STR("bar")); +	CHECK_XPATH_STRING(c, STR("translate('BAR','Rab','TxX')"), STR("BAT")); +	CHECK_XPATH_STRING(c, STR("translate('zzaaazzz','abcz','ABC')"), STR("AAA")); +	CHECK_XPATH_STRING(c, STR("translate('ddaaadddd','abcd','ABCxy')"), STR("xxAAAxxxx")); +	CHECK_XPATH_STRING(c, STR("concat('a','b','c','d','ef')"), STR("abcdef")); +	CHECK_XPATH_STRING(c, STR("concat(a, b)"), STR("")); +	CHECK_XPATH_STRING(doc.child(STR("doc")), STR("concat(a, b)"), STR("ab")); +	CHECK_XPATH_STRING(doc.child(STR("doc")), STR("concat(a, b, c, d, e)"), STR("abcdef")); +	CHECK_XPATH_STRING(c, STR("concat('cd','34')"), STR("cd34")); +	CHECK_XPATH_STRING(c, STR("concat('cd',34)"), STR("cd34")); +	CHECK_XPATH_STRING(c, STR("concat('bc',string(23))"), STR("bc23")); +	CHECK_XPATH_STRING(c, STR("concat(a,34)"), STR("34")); +	CHECK_XPATH_STRING(doc.child(STR("doc")), STR("concat(a,34)"), STR("a34")); +	CHECK_XPATH_STRING(c, STR("concat(false(),'ly')"), STR("falsely")); +	CHECK_XPATH_FAIL(STR("concat(/*)")); +	CHECK_XPATH_STRING(doc.child(STR("doc")), STR("concat(/*, '')"), STR("abcdefwhat's up")); +	CHECK_XPATH_STRING(doc.child(STR("doc")), STR("concat(/*, /*[@attr='whatsup'])"), STR("abcdefwhat's up")); +	CHECK_XPATH_STRING(doc.child(STR("doc")), STR("concat(/*, //*[@attr='whatsup'])"), STR("abcdefwhat's upwhat's up")); +	CHECK_XPATH_STRING(c, STR("substring('ENCYCLOPEDIA', 8, 3)"), STR("PED")); +	CHECK_XPATH_STRING(c, STR("substring('ENCYCLOPEDIA', 8)"), STR("PEDIA")); +	CHECK_XPATH_STRING(c, STR("substring('abcdefghijk',0 div 0, 5)"), STR("")); +	CHECK_XPATH_STRING(c, STR("substring('abcdefghijk',4, 6)"), STR("defghi")); +	CHECK_XPATH_STRING(c, STR("substring('1999/04/01', 1, 0)"), STR("")); +	CHECK_XPATH_STRING(c, STR("translate(normalize-space('     bar       fly        '), ' ', '_')"), STR("bar_fly")); +	CHECK_XPATH_STRING(c, STR("translate('barter','abe','bao')"), STR("abrtor")); +	CHECK_XPATH_STRING(c, STR("translate('barbarity', 'aeiouy', '******')"), STR("b*rb*r*t*")); +	CHECK_XPATH_STRING(doc, STR("translate(cd, concat(\"aqu'\", '\"eos'), 'AQU-+EOS')"), STR("QUA-lit+y")); +	CHECK_XPATH_STRING(c, STR("translate('quan+ti-ty', 'AQU-+EOS', concat(\"aqu'\", '\"eos'))"), STR("quan\"ti'ty")); +} + +static std::basic_string<char_t> number_to_string(int number) +{ +	std::basic_string<char_t> result; + +	while (number) +	{ +		result = static_cast<char_t>('0' + number % 10) + result; +		number /= 10; +	} + +	return result; +} + +TEST(xpath_xalan_string_5) +{ +	std::basic_string<char_t> query = STR("concat("); + +	for (int i = 1; i < 1000; ++i) +	{ +		query += STR("concat('t',"); +		query += number_to_string(i); +		query += STR("), "); +	} + +	query += STR("'')"); + +	std::basic_string<char_t> expected; + +	for (int j = 1; j < 1000; ++j) +	{ +		expected += STR("t"); +		expected += number_to_string(j); +	} + +	CHECK_XPATH_STRING(xml_node(), query.c_str(), expected.c_str()); +} + +TEST(xpath_xalan_string_6) +{ +	xml_node c; + +	CHECK_XPATH_STRING(c, STR("string(1)"), STR("1")); +	CHECK_XPATH_STRING(c, STR("string(12)"), STR("12")); +	CHECK_XPATH_STRING(c, STR("string(123)"), STR("123")); +	CHECK_XPATH_STRING(c, STR("string(1234)"), STR("1234")); +	CHECK_XPATH_STRING(c, STR("string(12345)"), STR("12345")); +	CHECK_XPATH_STRING(c, STR("string(123456)"), STR("123456")); +	CHECK_XPATH_STRING(c, STR("string(1234567)"), STR("1234567")); +	CHECK_XPATH_STRING(c, STR("string(12345678)"), STR("12345678")); +	CHECK_XPATH_STRING(c, STR("string(123456789)"), STR("123456789")); +	CHECK_XPATH_STRING(c, STR("string(1234567890)"), STR("1234567890")); +	CHECK_XPATH_STRING(c, STR("string(12345678901)"), STR("12345678901")); +	CHECK_XPATH_STRING(c, STR("string(123456789012)"), STR("123456789012")); +	CHECK_XPATH_STRING(c, STR("string(1234567890123)"), STR("1234567890123")); +	CHECK_XPATH_STRING(c, STR("string(12345678901234)"), STR("12345678901234")); +	CHECK_XPATH_STRING(c, STR("string(123456789012345)"), STR("123456789012345")); +	CHECK_XPATH_STRING(c, STR("string(1234567890123456)"), STR("1234567890123456")); +	CHECK_XPATH_STRING(c, STR("string(-1)"), STR("-1")); +	CHECK_XPATH_STRING(c, STR("string(-12)"), STR("-12")); +	CHECK_XPATH_STRING(c, STR("string(-123)"), STR("-123")); +	CHECK_XPATH_STRING(c, STR("string(-1234)"), STR("-1234")); +	CHECK_XPATH_STRING(c, STR("string(-12345)"), STR("-12345")); +	CHECK_XPATH_STRING(c, STR("string(-123456)"), STR("-123456")); +	CHECK_XPATH_STRING(c, STR("string(-1234567)"), STR("-1234567")); +	CHECK_XPATH_STRING(c, STR("string(-12345678)"), STR("-12345678")); +	CHECK_XPATH_STRING(c, STR("string(-123456789)"), STR("-123456789")); +	CHECK_XPATH_STRING(c, STR("string(-1234567890)"), STR("-1234567890")); +	CHECK_XPATH_STRING(c, STR("string(-12345678901)"), STR("-12345678901")); +	CHECK_XPATH_STRING(c, STR("string(-123456789012)"), STR("-123456789012")); +	CHECK_XPATH_STRING(c, STR("string(-1234567890123)"), STR("-1234567890123")); +	CHECK_XPATH_STRING(c, STR("string(-12345678901234)"), STR("-12345678901234")); +	CHECK_XPATH_STRING(c, STR("string(-123456789012345)"), STR("-123456789012345")); +	CHECK_XPATH_STRING(c, STR("string(-1234567890123456)"), STR("-1234567890123456")); +} + +#if 0 // $ this test requires round-to-nearest behavior in string->number conversion during parsing; atof gives us truncation +TEST(xpath_xalan_string_6_rounding) +{ +	xml_node c; + +	CHECK_XPATH_STRING(c, STR("string(12345678901234567)"), STR("12345678901234568")); +	CHECK_XPATH_STRING(c, STR("string(123456789012345678)"), STR("123456789012345680")); +	CHECK_XPATH_STRING(c, STR("string(-12345678901234567)"), STR("-12345678901234568")); +	CHECK_XPATH_STRING(c, STR("string(-123456789012345678)"), STR("-123456789012345680")); +} +#endif + +TEST(xpath_xalan_string_7) +{ +	xml_node c; + +	CHECK_XPATH_STRING(c, STR("string(.1)"), STR("0.1")); +	CHECK_XPATH_STRING(c, STR("string(.01)"), STR("0.01")); +	CHECK_XPATH_STRING(c, STR("string(.012)"), STR("0.012")); +	CHECK_XPATH_STRING(c, STR("string(.0123)"), STR("0.0123")); +	CHECK_XPATH_STRING(c, STR("string(.01234)"), STR("0.01234")); +	CHECK_XPATH_STRING(c, STR("string(.012345)"), STR("0.012345")); +	CHECK_XPATH_STRING(c, STR("string(.0123456)"), STR("0.0123456")); +	CHECK_XPATH_STRING(c, STR("string(.01234567)"), STR("0.01234567")); +	CHECK_XPATH_STRING(c, STR("string(.012345678)"), STR("0.012345678")); +	CHECK_XPATH_STRING(c, STR("string(.0123456789)"), STR("0.0123456789")); +	CHECK_XPATH_STRING(c, STR("string(.10123456789)"), STR("0.10123456789")); +	CHECK_XPATH_STRING(c, STR("string(.101234567892)"), STR("0.101234567892")); +	CHECK_XPATH_STRING(c, STR("string(.1012345678923)"), STR("0.1012345678923")); +	CHECK_XPATH_STRING(c, STR("string(.10123456789234)"), STR("0.10123456789234")); +	CHECK_XPATH_STRING(c, STR("string(.101234567892345)"), STR("0.101234567892345")); +	CHECK_XPATH_STRING(c, STR("string(.1012345678923456)"), STR("0.1012345678923456")); +	CHECK_XPATH_STRING(c, STR("string(-.1)"), STR("-0.1")); +	CHECK_XPATH_STRING(c, STR("string(-.01)"), STR("-0.01")); +	CHECK_XPATH_STRING(c, STR("string(-.012)"), STR("-0.012")); +	CHECK_XPATH_STRING(c, STR("string(-.0123)"), STR("-0.0123")); +	CHECK_XPATH_STRING(c, STR("string(-.01234)"), STR("-0.01234")); +	CHECK_XPATH_STRING(c, STR("string(-.012345)"), STR("-0.012345")); +	CHECK_XPATH_STRING(c, STR("string(-.0123456)"), STR("-0.0123456")); +	CHECK_XPATH_STRING(c, STR("string(-.01234567)"), STR("-0.01234567")); +	CHECK_XPATH_STRING(c, STR("string(-.012345678)"), STR("-0.012345678")); +	CHECK_XPATH_STRING(c, STR("string(-.0123456789)"), STR("-0.0123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.10123456789)"), STR("-0.10123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.101234567892)"), STR("-0.101234567892")); +	CHECK_XPATH_STRING(c, STR("string(-.1012345678923)"), STR("-0.1012345678923")); +	CHECK_XPATH_STRING(c, STR("string(-.10123456789234)"), STR("-0.10123456789234")); +	CHECK_XPATH_STRING(c, STR("string(-.101234567892345)"), STR("-0.101234567892345")); +	CHECK_XPATH_STRING(c, STR("string(-.1012345678923456)"), STR("-0.1012345678923456")); +} + +#if 0 // $ this test requires 16 decimal digits of mantissa in number->string conversion; we have 15 since only 15 is guaranteed, and 16 introduces 'garbage' digits in common cases like 0.4 +TEST(xpath_xalan_string_7_precision) +{ +	xml_node c; + +	CHECK_XPATH_STRING(c, STR("string(.10123456789234567)"), STR("0.10123456789234567")); +	CHECK_XPATH_STRING(c, STR("string(.101234567892345678)"), STR("0.10123456789234568")); +	CHECK_XPATH_STRING(c, STR("string(.1012345678923456789)"), STR("0.10123456789234568")); +	CHECK_XPATH_STRING(c, STR("string(.10123456789234567893)"), STR("0.10123456789234568")); +	CHECK_XPATH_STRING(c, STR("string(-.10123456789234567)"), STR("-0.10123456789234567")); +	CHECK_XPATH_STRING(c, STR("string(-.101234567892345678)"), STR("-0.10123456789234568")); +	CHECK_XPATH_STRING(c, STR("string(-.1012345678923456789)"), STR("-0.10123456789234568")); +	CHECK_XPATH_STRING(c, STR("string(-.10123456789234567893)"), STR("-0.10123456789234568")); +} +#endif + +TEST(xpath_xalan_string_8) +{ +	xml_node c; + +	// $ originally all last digits were 5's; a fully compliant implementation should correctly convert those as well, +	// however some of these failed because of atof truncation +	CHECK_XPATH_STRING(c, STR("string(9.87654321012344)"), STR("9.87654321012344")); +	CHECK_XPATH_STRING(c, STR("string(98.7654321012345)"), STR("98.7654321012345")); +	CHECK_XPATH_STRING(c, STR("string(987.654321012345)"), STR("987.654321012345")); +	CHECK_XPATH_STRING(c, STR("string(9876.54321012344)"), STR("9876.54321012344")); +	CHECK_XPATH_STRING(c, STR("string(98765.4321012345)"), STR("98765.4321012345")); +	CHECK_XPATH_STRING(c, STR("string(987654.321012345)"), STR("987654.321012345")); +	CHECK_XPATH_STRING(c, STR("string(9876543.21012345)"), STR("9876543.21012345")); +	CHECK_XPATH_STRING(c, STR("string(98765432.1012345)"), STR("98765432.1012345")); +	CHECK_XPATH_STRING(c, STR("string(987654321.012345)"), STR("987654321.012345")); +	CHECK_XPATH_STRING(c, STR("string(9876543210.12344)"), STR("9876543210.12344")); +	CHECK_XPATH_STRING(c, STR("string(98765432101.2345)"), STR("98765432101.2345")); +	CHECK_XPATH_STRING(c, STR("string(987654321012.345)"), STR("987654321012.345")); +	CHECK_XPATH_STRING(c, STR("string(9876543210123.43)"), STR("9876543210123.43")); +	CHECK_XPATH_STRING(c, STR("string(98765432101234.5)"), STR("98765432101234.5")); + +	CHECK_XPATH_STRING(c, STR("string(-9.87654321012344)"), STR("-9.87654321012344")); +	CHECK_XPATH_STRING(c, STR("string(-98.7654321012345)"), STR("-98.7654321012345")); +	CHECK_XPATH_STRING(c, STR("string(-987.654321012345)"), STR("-987.654321012345")); +	CHECK_XPATH_STRING(c, STR("string(-9876.54321012344)"), STR("-9876.54321012344")); +	CHECK_XPATH_STRING(c, STR("string(-98765.4321012345)"), STR("-98765.4321012345")); +	CHECK_XPATH_STRING(c, STR("string(-987654.321012345)"), STR("-987654.321012345")); +	CHECK_XPATH_STRING(c, STR("string(-9876543.21012345)"), STR("-9876543.21012345")); +	CHECK_XPATH_STRING(c, STR("string(-98765432.1012345)"), STR("-98765432.1012345")); +	CHECK_XPATH_STRING(c, STR("string(-987654321.012345)"), STR("-987654321.012345")); +	CHECK_XPATH_STRING(c, STR("string(-9876543210.12344)"), STR("-9876543210.12344")); +	CHECK_XPATH_STRING(c, STR("string(-98765432101.2345)"), STR("-98765432101.2345")); +	CHECK_XPATH_STRING(c, STR("string(-987654321012.345)"), STR("-987654321012.345")); +	CHECK_XPATH_STRING(c, STR("string(-9876543210123.43)"), STR("-9876543210123.43")); +	CHECK_XPATH_STRING(c, STR("string(-98765432101234.5)"), STR("-98765432101234.5")); +} + +TEST(xpath_xalan_string_9) +{ +	xml_node c; + +	CHECK_XPATH_STRING(c, STR("string(.123456789)"), STR("0.123456789")); +	CHECK_XPATH_STRING(c, STR("string(.0123456789)"), STR("0.0123456789")); +	CHECK_XPATH_STRING(c, STR("string(.00123456789)"), STR("0.00123456789")); +	CHECK_XPATH_STRING(c, STR("string(.000123456789)"), STR("0.000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.0000123456789)"), STR("0.0000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.00000123456789)"), STR("0.00000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.000000123456789)"), STR("0.000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.0000000123456789)"), STR("0.0000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.00000000123456789)"), STR("0.00000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.000000000123456789)"), STR("0.000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.0000000000123456789)"), STR("0.0000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.00000000000123456789)"), STR("0.00000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.000000000000123456789)"), STR("0.000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.0000000000000123456789)"), STR("0.0000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.00000000000000123456789)"), STR("0.00000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.000000000000000123456789)"), STR("0.000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.0000000000000000123456789)"), STR("0.0000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.00000000000000000123456789)"), STR("0.00000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.000000000000000000123456789)"), STR("0.000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.0000000000000000000123456789)"), STR("0.0000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.00000000000000000000123456789)"), STR("0.00000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.000000000000000000000123456789)"), STR("0.000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.0000000000000000000000123456789)"), STR("0.0000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.00000000000000000000000123456789)"), STR("0.00000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.000000000000000000000000123456789)"), STR("0.000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.0000000000000000000000000123456789)"), STR("0.0000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.00000000000000000000000000123456789)"), STR("0.00000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.000000000000000000000000000123456789)"), STR("0.000000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.0000000000000000000000000000123456789)"), STR("0.0000000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.00000000000000000000000000000123456789)"), STR("0.00000000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.000000000000000000000000000000123456789)"), STR("0.000000000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.0000000000000000000000000000000123456789)"), STR("0.0000000000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.00000000000000000000000000000000123456789)"), STR("0.00000000000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.000000000000000000000000000000000123456789)"), STR("0.000000000000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.0000000000000000000000000000000000123456789)"), STR("0.0000000000000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.00000000000000000000000000000000000123456789)"), STR("0.00000000000000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.000000000000000000000000000000000000123456789)"), STR("0.000000000000000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.0000000000000000000000000000000000000123456789)"), STR("0.0000000000000000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.00000000000000000000000000000000000000123456789)"), STR("0.00000000000000000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.000000000000000000000000000000000000000123456789)"), STR("0.000000000000000000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(.0000000000000000000000000000000000000000123456789)"), STR("0.0000000000000000000000000000000000000000123456789")); + +	CHECK_XPATH_STRING(c, STR("string(-.123456789)"), STR("-0.123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.0123456789)"), STR("-0.0123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.00123456789)"), STR("-0.00123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.000123456789)"), STR("-0.000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.0000123456789)"), STR("-0.0000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.00000123456789)"), STR("-0.00000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.000000123456789)"), STR("-0.000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.0000000123456789)"), STR("-0.0000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.00000000123456789)"), STR("-0.00000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.000000000123456789)"), STR("-0.000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.0000000000123456789)"), STR("-0.0000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.00000000000123456789)"), STR("-0.00000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.000000000000123456789)"), STR("-0.000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.0000000000000123456789)"), STR("-0.0000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.00000000000000123456789)"), STR("-0.00000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.000000000000000123456789)"), STR("-0.000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.0000000000000000123456789)"), STR("-0.0000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.00000000000000000123456789)"), STR("-0.00000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.000000000000000000123456789)"), STR("-0.000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.0000000000000000000123456789)"), STR("-0.0000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.00000000000000000000123456789)"), STR("-0.00000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.000000000000000000000123456789)"), STR("-0.000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.0000000000000000000000123456789)"), STR("-0.0000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.00000000000000000000000123456789)"), STR("-0.00000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.000000000000000000000000123456789)"), STR("-0.000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.0000000000000000000000000123456789)"), STR("-0.0000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.00000000000000000000000000123456789)"), STR("-0.00000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.000000000000000000000000000123456789)"), STR("-0.000000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.0000000000000000000000000000123456789)"), STR("-0.0000000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.00000000000000000000000000000123456789)"), STR("-0.00000000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.000000000000000000000000000000123456789)"), STR("-0.000000000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.0000000000000000000000000000000123456789)"), STR("-0.0000000000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.00000000000000000000000000000000123456789)"), STR("-0.00000000000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.000000000000000000000000000000000123456789)"), STR("-0.000000000000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.0000000000000000000000000000000000123456789)"), STR("-0.0000000000000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.00000000000000000000000000000000000123456789)"), STR("-0.00000000000000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.000000000000000000000000000000000000123456789)"), STR("-0.000000000000000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.0000000000000000000000000000000000000123456789)"), STR("-0.0000000000000000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.00000000000000000000000000000000000000123456789)"), STR("-0.00000000000000000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.000000000000000000000000000000000000000123456789)"), STR("-0.000000000000000000000000000000000000000123456789")); +	CHECK_XPATH_STRING(c, STR("string(-.0000000000000000000000000000000000000000123456789)"), STR("-0.0000000000000000000000000000000000000000123456789")); +} + +#endif diff --git a/tests/test_xpath_xalan_3.cpp b/tests/test_xpath_xalan_3.cpp index ee9253d..54b8a62 100644 --- a/tests/test_xpath_xalan_3.cpp +++ b/tests/test_xpath_xalan_3.cpp @@ -1,319 +1,319 @@ -#ifndef PUGIXML_NO_XPATH
 -
 -#include "common.hpp"
 -
 -TEST_XML(xpath_xalan_axes_1, "<far-north><north-north-west1/><north-north-west2/><north><near-north><far-west/><west/><near-west/><center center-attr-1='c1' center-attr-2='c2' center-attr-3='c3'><near-south-west/><near-south><south><far-south/></south></near-south><near-south-east/></center><near-east/><east/><far-east/></near-north></north><north-north-east1/><north-north-east2/></far-north>")
 -{
 -	xml_node center = doc.select_single_node(STR("//center")).node();
 -
 -	CHECK_XPATH_NODESET(center, STR("self::*[near-south]")) % 10;
 -	CHECK_XPATH_NODESET(center, STR("self::*[@center-attr-2]")) % 10;
 -	CHECK_XPATH_NODESET(center, STR("preceding-sibling::*")) % 9 % 8 % 7;
 -	CHECK_XPATH_NODESET(center, STR("preceding-sibling::*/following-sibling::*")) % 8 % 9 % 10 % 19 % 20 % 21;
 -	CHECK_XPATH_NODESET(center, STR("preceding-sibling::*[2]/following-sibling::*")) % 9 % 10 % 19 % 20 % 21;
 -	CHECK_XPATH_NODESET(center, STR("preceding-sibling::*[2]/following-sibling::*[4]")) % 20;
 -	CHECK_XPATH_NODESET(center, STR("preceding-sibling::*[2]/following-sibling::*[4]/preceding-sibling::*[5]/following-sibling::*[4]/following-sibling::*[2]")) % 21;
 -	CHECK_XPATH_NODESET(center, STR("following-sibling::*")) % 19 % 20 % 21;
 -	CHECK_XPATH_NODESET(center, STR("following-sibling::*/preceding-sibling::*")) % 7 % 8 % 9 % 10 % 19 % 20;
 -	CHECK_XPATH_NODESET(center, STR("following-sibling::*[2]/preceding-sibling::*")) % 19 % 10 % 9 % 8 % 7;
 -	CHECK_XPATH_NODESET(center, STR("following-sibling::*[2]/preceding-sibling::*[4]")) % 8;
 -	CHECK_XPATH_NODESET(center, STR("following-sibling::*[2]/preceding-sibling::*[4]/following-sibling::*[5]/preceding-sibling::*[4]/preceding-sibling::*[2]")) % 7;
 -	CHECK_XPATH_NODESET(center, STR("following::*[4]/../*[2]")) % 4;
 -	CHECK_XPATH_NODESET(center, STR("preceding::*[2]/../following::*")) % 22 % 23;
 -	CHECK_XPATH_NODESET(center, STR("preceding::*[2]/../descendant::*[10]/following-sibling::east")) % 20;
 -	CHECK_XPATH_NODESET(center, STR("//*")) % 2 % 3 % 4 % 5 % 6 % 7 % 8 % 9 % 10 % 14 % 15 % 16 % 17 % 18 % 19 % 20 % 21 % 22 % 23;
 -	CHECK_XPATH_NODESET(center, STR("//ancestor::*")) % 2 % 5 % 6 % 10 % 15 % 16;
 -	CHECK_XPATH_NODESET(center, STR("//*[count(ancestor::*) >= 2]/../parent::*")) % 2 % 5 % 6 % 10 % 15;
 -	CHECK_XPATH_NODESET(center, STR("//*[count(./*/*) > 0]")) % 2 % 5 % 6 % 10 % 15;
 -	CHECK_XPATH_NODESET(center, STR("@*/ancestor::*")) % 2 % 5 % 6 % 10;
 -	CHECK_XPATH_NODESET(center, STR("@*/following::*")) % 14 % 15 % 16 % 17 % 18 % 19 % 20 % 21 % 22 % 23;
 -	CHECK_XPATH_NODESET(center, STR("@*/preceding::*")) % 3 % 4 % 7 % 8 % 9;
 -	CHECK_XPATH_NODESET(center, STR("preceding-sibling::*|following-sibling::*")) % 7 % 8 % 9 % 19 % 20 % 21;
 -	CHECK_XPATH_NODESET(center, STR("(preceding-sibling::*|following-sibling::*)/ancestor::*[last()]/*[last()]")) % 23;
 -	CHECK_XPATH_NODESET(center, STR(".//near-south/preceding-sibling::*|following-sibling::east/ancestor-or-self::*[2]")) % 6 % 14;
 -}
 -
 -TEST_XML_FLAGS(xpath_xalan_axes_2, "<far-north> Level-1<north-north-west1/><north-north-west2/><!-- Comment-2 --> Level-2<?a-pi pi-2?><north><!-- Comment-3 --> Level-3<?a-pi pi-3?><near-north><far-west/><west/><near-west/><!-- Comment-4 --> Level-4<?a-pi pi-4?><center center-attr-1='c1' center-attr-2='c2' center-attr-3='c3'><near-south-west/><!--Comment-5-->   Level-5<?a-pi pi-5?><near-south><!--Comment-6-->   Level-6<?a-pi pi-6?><south attr1='First' attr2='Last'> <far-south/></south></near-south><near-south-east/></center><near-east/><east/><far-east/></near-north></north><north-north-east1/><north-north-east2/></far-north>", parse_default | parse_comments | parse_pi)
 -{
 -	xml_node center = doc.select_single_node(STR("//center")).node();
 -
 -	CHECK_XPATH_NODESET(center, STR("@*")) % 21 % 22 % 23;
 -	CHECK_XPATH_NODESET(center, STR("@*/child::*"));
 -	CHECK_XPATH_NODESET(center, STR("@*/descendant::node()"));
 -	CHECK_XPATH_NODESET(center, STR("@*/parent::node()")) % 20;
 -	CHECK_XPATH_NODESET(center, STR("@*/ancestor::node()")) % 1 % 2 % 9 % 13 % 20;
 -	CHECK_XPATH_NODESET(center, STR("@*/self::node()")) % 21 % 22 % 23;
 -	CHECK_XPATH_NODESET(center, STR("@*/.")) % 21 % 22 % 23;
 -	CHECK_XPATH_NODESET(center, STR("@*/descendant-or-self::node()")) % 21 % 22 % 23;
 -	CHECK_XPATH_NODESET(center, STR("@*/ancestor-or-self::node()")) % 1 % 2 % 9 % 13 % 20 % 21 % 22 % 23;
 -	CHECK_XPATH_NODESET(center, STR("@*/ancestor-or-self::*")) % 2 % 9 % 13 % 20;
 -	CHECK_XPATH_NODESET(center, STR("@*/preceding-sibling::node()"));
 -	CHECK_XPATH_NODESET(center, STR("@*/following-sibling::*"));
 -	CHECK_XPATH_NODESET(center, STR("@*/ancestor::*/near-north/*[4]/@*/preceding::*")) % 4 % 5 % 14 % 15 % 16;
 -	CHECK_XPATH_NODESET(center, STR("@*/ancestor::*/near-north/*[4]/@*/preceding::comment()")) % 6 % 10 % 17;
 -	CHECK_XPATH_NODESET(center, STR("@*/ancestor::*/near-north/*[4]/@*/preceding::text()")) % 3 % 7 % 11 % 18;
 -	CHECK_XPATH_NODESET(center, STR("@*/ancestor::*/near-north/*[4]/@*/preceding::processing-instruction()")) % 8 % 12 % 19;
 -	CHECK_XPATH_NODESET(center, STR("@*/following::comment()")) % 25 % 29;
 -	CHECK_XPATH_NODESET(center, STR("@*/following::processing-instruction()")) % 27 % 31;
 -	CHECK_XPATH_NODESET(center, STR("@*/following::text()")) % 26 % 30;
 -	CHECK_XPATH_NODESET(center, STR("@*/ancestor::*/near-north/*[4]/@*/preceding::node()")) % 3 % 4 % 5 % 6 % 7 % 8 % 10 % 11 % 12 % 14 % 15 % 16 % 17 % 18 % 19;
 -	CHECK_XPATH_NODESET(center, STR("@*/ancestor::*/near-north/*[4]/@*/following::node()")) % 24 % 25 % 26 % 27 % 28 % 29 % 30 % 31 % 32 % 35 % 36 % 37 % 38 % 39 % 40 % 41;
 -
 -	CHECK_XPATH_NODESET(center, STR("(//comment())[1]/..")) % 2;
 -	CHECK_XPATH_NODESET(center, STR("(//attribute::*)[1]/../..")) % 13;
 -}
 -
 -TEST_XML(xpath_xalan_axes_3, "<far-north><north><near-north><far-west/><west/><near-west/><center><near-south><south><far-south/></south></near-south></center><near-east/><east/><far-east/></near-north></north></far-north>")
 -{
 -	xml_node center = doc.select_single_node(STR("//center")).node();
 -
 -	CHECK_XPATH_NODESET(center, STR("ancestor-or-self::*")) % 8 % 4 % 3 % 2;
 -	CHECK_XPATH_NODESET(center, STR("ancestor::*[3]")) % 2;
 -	CHECK_XPATH_NODESET(center, STR("ancestor-or-self::*[1]")) % 8;
 -	CHECK_XPATH_NODESET(center, STR("@*[2]"));
 -	CHECK_XPATH_NODESET(center, STR("child::*[2]"));
 -	CHECK_XPATH_NODESET(center, STR("child::near-south-west"));
 -	CHECK_XPATH_NODESET(center, STR("descendant::*[3]")) % 11;
 -	CHECK_XPATH_NODESET(center, STR("descendant::far-south")) % 11;
 -	CHECK_XPATH_NODESET(center, STR("descendant-or-self::*[3]")) % 10;
 -	CHECK_XPATH_NODESET(center, STR("descendant-or-self::far-south")) % 11;
 -	CHECK_XPATH_NODESET(center, STR("descendant-or-self::center")) % 8;
 -	CHECK_XPATH_NODESET(center, STR("following::*[4]"));
 -	CHECK_XPATH_NODESET(center, STR("following::out-yonder-east"));
 -	CHECK_XPATH_NODESET(center, STR("preceding::*[4]"));
 -	CHECK_XPATH_NODESET(center, STR("preceding::out-yonder-west"));
 -	CHECK_XPATH_NODESET(center, STR("following-sibling::*[2]")) % 13;
 -	CHECK_XPATH_NODESET(center, STR("following-sibling::east")) % 13;
 -	CHECK_XPATH_NODESET(center, STR("preceding-sibling::*[2]")) % 6;
 -	CHECK_XPATH_NODESET(center, STR("preceding-sibling::west")) % 6;
 -	CHECK_XPATH_NODESET(center, STR("parent::near-north")) % 4;
 -	CHECK_XPATH_NODESET(center, STR("parent::*[1]")) % 4;
 -	CHECK_XPATH_NODESET(center, STR("parent::foo"));
 -	CHECK_XPATH_NODESET(center, STR("..")) % 4;
 -	CHECK_XPATH_NODESET(center, STR("self::center")) % 8;
 -	CHECK_XPATH_NODESET(center, STR("self::*[1]")) % 8;
 -	CHECK_XPATH_NODESET(center, STR("self::foo"));
 -	CHECK_XPATH_NODESET(center, STR(".")) % 8;
 -	CHECK_XPATH_NODESET(center, STR("/far-north/north/near-north/center/ancestor-or-self::*")) % 8 % 4 % 3 % 2;
 -}
 -
 -TEST_XML(xpath_xalan_axes_4, "<far-north><north><near-north><far-west/><west/><near-west/><center><near-south><south><far-south/></south></near-south></center><near-east/><east/><far-east/></near-north></north></far-north>")
 -{
 -	xml_node north = doc.select_single_node(STR("//north")).node();
 -
 -	CHECK_XPATH_STRING(north, STR("name(/descendant-or-self::north)"), STR("north"));
 -	CHECK_XPATH_STRING(north, STR("name(/descendant::near-north)"), STR("near-north"));
 -	CHECK_XPATH_STRING(north, STR("name(self::node()/descendant-or-self::north)"), STR("north"));
 -	CHECK_XPATH_STRING(north, STR("name(self::node()/descendant::near-north)"), STR("near-north"));
 -	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::north/descendant-or-self::north)"), STR("north"));
 -	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::north/descendant::near-north)"), STR("near-north"));
 -	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::north/child::near-north)"), STR("near-north"));
 -	CHECK_XPATH_STRING(north, STR("name(descendant::near-north/descendant-or-self::near-north)"), STR("near-north"));
 -	CHECK_XPATH_STRING(north, STR("name(descendant::near-north/descendant::far-west)"), STR("far-west"));
 -
 -	CHECK_XPATH_STRING(north, STR("name(/descendant-or-self::north/descendant-or-self::north)"), STR("north"));
 -	CHECK_XPATH_STRING(north, STR("name(/descendant-or-self::north/child::near-north)"), STR("near-north"));
 -	CHECK_XPATH_STRING(north, STR("name(/descendant::near-north/descendant-or-self::near-north)"), STR("near-north"));
 -	CHECK_XPATH_STRING(north, STR("name(self::node()/descendant-or-self::north/descendant-or-self::north)"), STR("north"));
 -	CHECK_XPATH_STRING(north, STR("name(self::node()/descendant-or-self::north/child::near-north)"), STR("near-north"));
 -	CHECK_XPATH_STRING(north, STR("name(self::node()/descendant::near-north/descendant-or-self::far-west)"), STR("far-west"));
 -	CHECK_XPATH_STRING(north, STR("name(self::node()/descendant::near-north/child::far-west)"), STR("far-west"));
 -	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::north/descendant-or-self::north/descendant-or-self::north)"), STR("north"));
 -	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::north/descendant-or-self::north/child::near-north)"), STR("near-north"));
 -	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::north/descendant::near-north/descendant-or-self::near-north)"), STR("near-north"));
 -	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::north/descendant::near-north/child::far-west)"), STR("far-west"));
 -	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::north/child::near-north/descendant-or-self::near-north)"), STR("near-north"));
 -	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::north/child::near-north/child::far-west)"), STR("far-west"));
 -	CHECK_XPATH_STRING(north, STR("name(descendant::near-north/descendant-or-self::near-north/descendant-or-self::near-north)"), STR("near-north"));
 -	CHECK_XPATH_STRING(north, STR("name(descendant::near-north/descendant-or-self::near-north/child::far-west)"), STR("far-west"));
 -	CHECK_XPATH_STRING(north, STR("name(descendant::near-north/descendant::far-west/descendant-or-self::far-west)"), STR("far-west"));
 -
 -	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::node()/descendant-or-self::north)"), STR("north"));
 -	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::node()/descendant::near-north)"), STR("near-north"));
 -	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::node()/child::near-north)"), STR("near-north"));
 -	CHECK_XPATH_STRING(north, STR("name(descendant::node()/descendant-or-self::near-north)"), STR("near-north"));
 -	CHECK_XPATH_STRING(north, STR("name(descendant::node()/descendant::far-west)"), STR("far-west"));
 -
 -	CHECK_XPATH_STRING(north, STR("name(/descendant-or-self::node()/descendant-or-self::north)"), STR("north"));
 -	CHECK_XPATH_STRING(north, STR("name(/descendant-or-self::node()/child::near-north)"), STR("near-north"));
 -	CHECK_XPATH_STRING(north, STR("name(/descendant::node()/descendant-or-self::near-north)"), STR("near-north"));
 -	CHECK_XPATH_STRING(north, STR("name(self::node()/descendant-or-self::node()/descendant-or-self::north)"), STR("north"));
 -	CHECK_XPATH_STRING(north, STR("name(self::node()/descendant-or-self::node()/child::near-north)"), STR("near-north"));
 -	CHECK_XPATH_STRING(north, STR("name(self::node()/descendant::node()/descendant-or-self::far-west)"), STR("far-west"));
 -	CHECK_XPATH_STRING(north, STR("name(self::node()/descendant::node()/child::far-west)"), STR("far-west"));
 -	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::node()/descendant-or-self::node()/descendant-or-self::north)"), STR("north"));
 -	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::node()/descendant-or-self::node()/child::near-north)"), STR("near-north"));
 -	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::node()/descendant::node()/descendant-or-self::near-north)"), STR("near-north"));
 -	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::node()/descendant::node()/child::far-west)"), STR("far-west"));
 -	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::node()/child::node()/descendant-or-self::near-north)"), STR("near-north"));
 -	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::node()/child::node()/child::far-west)"), STR("far-west"));
 -	CHECK_XPATH_STRING(north, STR("name(descendant::node()/descendant-or-self::node()/descendant-or-self::near-north)"), STR("near-north"));
 -	CHECK_XPATH_STRING(north, STR("name(descendant::node()/descendant-or-self::node()/child::far-west)"), STR("far-west"));
 -	CHECK_XPATH_STRING(north, STR("name(descendant::node()/descendant::node()/descendant-or-self::far-west)"), STR("far-west"));
 -}
 -
 -TEST_XML_FLAGS(xpath_xalan_axes_5, "<text>text</text><comment><!--comment--></comment><pi><?pi?></pi>", parse_default | parse_comments | parse_pi)
 -{
 -	CHECK_XPATH_NODESET(doc, STR("text/self::text()"));
 -	CHECK_XPATH_NODESET(doc, STR("comment/self::comment()"));
 -	CHECK_XPATH_NODESET(doc, STR("pi/self::processing-instruction()"));
 -}
 -
 -TEST_XML(xpath_xalan_axes_6, "<doc><T>Test for source tree depth</T><a><T>A</T><b><T>B</T><c><T>C</T><d><T>D</T><e><T>E</T><f><T>F</T><g><T>G</T><h><T>H</T><i><T>I</T><j><T>J</T><k><T>K</T><l><T>L</T><m><T>M</T><n><T>N</T><o><T>O</T></o></n></m></l></k></j></i></h></g></f></e></d></c></b></a></doc>")
 -{
 -	CHECK_XPATH_NODESET(doc, STR("//T")) % 3 % 6 % 9 % 12 % 15 % 18 % 21 % 24 % 27 % 30 % 33 % 36 % 39 % 42 % 45 % 48;
 -}
 -
 -TEST_XML(xpath_xalan_axes_7, "<far-north><north><near-north><far-west/><west/><near-west/><center center-attr-1='c1' center-attr-2='c2' center-attr-3='c3'><near-south><south><far-south/></south></near-south></center><near-east/><east/><far-east/></near-north></north></far-north>")
 -{
 -	xml_node center = doc.select_single_node(STR("//center")).node();
 -
 -	CHECK_XPATH_NODESET(center, STR("attribute::*[2]")) % 10;
 -	CHECK_XPATH_NODESET(center, STR("@*")) % 9 % 10 % 11;
 -	CHECK_XPATH_NODESET(center, STR("child::*/child::*")) % 13;
 -	CHECK_XPATH_NODESET(center, STR("child::*/descendant::*")) % 13 % 14;
 -	CHECK_XPATH_NODESET(center, STR("descendant::*/child::*")) % 13 % 14;
 -}
 -
 -TEST_XML(xpath_xalan_axes_8, "<far-north><north><near-north><far-west/><west/><near-west/><center center-attr-1='c1' center-attr-2='c2' center-attr-3='c3'><near-south-east/><near-south><south><far-south/></south></near-south><near-south-west/></center><near-east/><east/><far-east/></near-north></north></far-north>")
 -{
 -	xml_node near_north = doc.select_single_node(STR("//near-north")).node();
 -
 -	CHECK_XPATH_NODESET(near_north, STR("center//child::*")) % 12 % 13 % 14 % 15 % 16;
 -	CHECK_XPATH_NODESET(near_north, STR("center//descendant::*")) % 12 % 13 % 14 % 15 % 16;
 -	CHECK_XPATH_NODESET(near_north, STR("center/descendant::*")) % 12 % 13 % 14 % 15 % 16;
 -	CHECK_XPATH_NODESET(near_north, STR("center/child::*")) % 12 % 13 % 16;
 -	CHECK_XPATH_NODESET(near_north, STR("center//*")) % 12 % 13 % 14 % 15 % 16;
 -}
 -
 -TEST_XML(xpath_xalan_axes_9, "<doc><foo att1='c'><foo att1='b'><foo att1='a'><baz/></foo></foo></foo><bar/></doc>")
 -{
 -	xml_node baz = doc.select_single_node(STR("//baz")).node();
 -
 -	CHECK_XPATH_NODESET(baz, STR("ancestor-or-self::*[@att1][1]/@att1")) % 8;
 -    CHECK_XPATH_NODESET(baz, STR("(ancestor-or-self::*)[@att1][1]/@att1")) % 4;
 -
 -	CHECK_XPATH_NODESET(baz, STR("ancestor::foo[1]/@att1")) % 8;
 -	CHECK_XPATH_NODESET(baz, STR("(ancestor::foo[1])/@att1")) % 8;
 -	CHECK_XPATH_NODESET(baz, STR("(ancestor::foo)[1]/@att1")) % 4;
 -	CHECK_XPATH_NODESET(baz, STR("((ancestor::foo))[1]/@att1")) % 4;
 -	CHECK_XPATH_NODESET(baz, STR("(((ancestor::foo)[1])/@att1)")) % 4;
 -
 -	xml_node bar = doc.child(STR("doc")).child(STR("bar"));
 -
 -    CHECK_XPATH_NODESET(bar, STR("preceding::foo[1]/@att1")) % 8;
 -	CHECK_XPATH_NODESET(bar, STR("(preceding::foo)[1]/@att1")) % 4;
 -}
 -
 -TEST_XML(xpath_xalan_axes_10, "<doc><foo att1='c'/><foo att1='b'/><foo att1='a'/><baz/></doc>")
 -{
 -	xml_node baz = doc.child(STR("doc")).child(STR("baz"));
 -
 -    CHECK_XPATH_NODESET(baz, STR("preceding-sibling::foo[1]/@att1")) % 8;
 -	CHECK_XPATH_NODESET(baz, STR("(preceding-sibling::foo)[1]/@att1")) % 4;
 -}
 -
 -TEST_XML(xpath_xalan_axes_11, "<chapter title='A' x='0'><section title='A1' x='1'><subsection title='A1a' x='2'>hello</subsection><subsection title='A1b'>ahoy</subsection></section><section title='A2'><subsection title='A2a'>goodbye</subsection><subsection title='A2b'>sayonara</subsection><subsection title='A2c'>adios</subsection></section><section title='A3'><subsection title='A3a'>aloha</subsection><subsection title='A3b'><footnote x='3'>A3b-1</footnote><footnote>A3b-2</footnote></subsection><subsection title='A3c'>shalom</subsection></section></chapter>")
 -{
 -	xml_node chapter = doc.child(STR("chapter"));
 -
 -    CHECK_XPATH_NUMBER(doc, STR("count(//@*)"), 16);
 -    CHECK_XPATH_NUMBER(doc, STR("count(//@title)"), 12);
 -    CHECK_XPATH_NUMBER(doc, STR("count(//section//@*)"), 14);
 -    CHECK_XPATH_NUMBER(doc, STR("count(//section//@title)"), 11);
 -
 -	CHECK_XPATH_NUMBER(chapter, STR("count(.//@*)"), 16);
 -	CHECK_XPATH_NUMBER(chapter, STR("count(.//@title)"), 12);
 -	CHECK_XPATH_NUMBER(chapter, STR("count(section[1]//@*)"), 5);
 -	CHECK_XPATH_NUMBER(chapter, STR("count(section[1]//@title)"), 3);
 -	CHECK_XPATH_NUMBER(chapter, STR("count(section[2]//@*)"), 4);
 -	CHECK_XPATH_NUMBER(chapter, STR("count(section[2]//@title)"), 4);
 -	CHECK_XPATH_NUMBER(chapter, STR("count(section[3]//@*)"), 5);
 -	CHECK_XPATH_NUMBER(chapter, STR("count(section[3]//@title)"), 4);
 -}
 -
 -TEST_XML_FLAGS(xpath_xalan_axes_12, "<far-north><north>north-text1<near-north><far-west/><west><!-- Western comment --></west><near-west/><center>center-text1<near-south><south>south-text</south></near-south><near-south-west/>center-text2</center><near-east/><east><!-- Eastern comment --></east><far-east/></near-north>north-text2</north></far-north>", parse_default | parse_comments)
 -{
 -	CHECK_XPATH_NODESET(doc, STR("/descendant::*")) % 2 % 3 % 5 % 6 % 7 % 9 % 10 % 12 % 13 % 15 % 17 % 18 % 20;
 -	CHECK_XPATH_NODESET(doc, STR("far-north/..//*")) % 2 % 3 % 5 % 6 % 7 % 9 % 10 % 12 % 13 % 15 % 17 % 18 % 20;
 -	CHECK_XPATH_NODESET(doc, STR("far-north/north/..//*")) % 3 % 5 % 6 % 7 % 9 % 10 % 12 % 13 % 15 % 17 % 18 % 20;
 -	CHECK_XPATH_NODESET(doc, STR("far-north/north-yonder/..//*"));
 -}
 -
 -TEST_XML(xpath_xalan_axes_13, "<doc att1='e'><foo att1='d'><foo att1='c'><foo att1='b'><baz att1='a'/></foo></foo></foo></doc>")
 -{
 -	xml_node d = doc.child(STR("doc"));
 -	xml_node baz = doc.select_single_node(STR("//baz")).node();
 -
 -	CHECK_XPATH_NUMBER(d, STR("count(descendant-or-self::*/@att1)"), 5);
 -	CHECK_XPATH_NODESET(d, STR("descendant-or-self::*/@att1[last()]")) % 3 % 5 % 7 % 9 % 11;
 -	CHECK_XPATH_STRING(d, STR("string(descendant-or-self::*/@att1[last()])"), STR("e"));
 -	CHECK_XPATH_NODESET(d, STR("descendant-or-self::*[last()]/@att1")) % 11;
 -	CHECK_XPATH_NODESET(d, STR("(descendant-or-self::*/@att1)[last()]")) % 11;
 -
 -	CHECK_XPATH_NUMBER(baz, STR("count(ancestor-or-self::*/@att1)"), 5);
 -    CHECK_XPATH_NODESET(baz, STR("ancestor-or-self::*/@att1[last()]")) % 3 % 5 % 7 % 9 % 11;
 -    CHECK_XPATH_STRING(baz, STR("string(ancestor-or-self::*/@att1[last()])"), STR("e"));
 -    CHECK_XPATH_NODESET(baz, STR("(ancestor-or-self::*)/@att1[last()]")) % 3 % 5 % 7 % 9 % 11;
 -    CHECK_XPATH_STRING(baz, STR("string((ancestor-or-self::*)/@att1[last()])"), STR("e"));
 -    CHECK_XPATH_NODESET(baz, STR("(ancestor-or-self::*/@att1)[last()]")) % 11;
 -    CHECK_XPATH_NODESET(baz, STR("(ancestor::*|self::*)/@att1[last()]")) % 3 % 5 % 7 % 9 % 11;
 -    CHECK_XPATH_STRING(baz, STR("string((ancestor::*|self::*)/@att1[last()])"), STR("e"));
 -	CHECK_XPATH_NODESET(baz, STR("((ancestor::*|self::*)/@att1)[last()]")) % 11;
 -}
 -
 -TEST_XML_FLAGS(xpath_xalan_axes_14, "<doc><n a='v'/><?pi?><!--comment-->text<center/>text<!--comment--><?pi?><n a='v'/></doc>", parse_default | parse_comments | parse_pi)
 -{
 -	CHECK_XPATH_NODESET(doc, STR("//center/preceding::node()")) % 7 % 6 % 5 % 3;
 -	CHECK_XPATH_NODESET(doc, STR("//center/following::node()")) % 9 % 10 % 11 % 12;
 -}
 -
 -TEST_XML(xpath_xalan_axes_15, "<doc><foo new='true'><baz>is new</baz></foo><foo new='true'>xyz<baz>is new but has text</baz></foo><foo new='false'><baz>is not new</baz></foo></doc>")
 -{
 -	CHECK_XPATH_NODESET(doc, STR("//text()[ancestor::*[@new='true'][not(text())]]")) % 6;
 -	CHECK_XPATH_NODESET(doc, STR("//text()[ancestor::*[2][@new]]")) % 6 % 11 % 15;
 -
 -	xml_node foo = doc.child(STR("doc")).child(STR("foo")).child(STR("baz")).first_child();
 -
 -	CHECK_XPATH_STRING(foo, STR("name(ancestor::*[3])"), STR("doc"));
 -	CHECK_XPATH_STRING(foo, STR("name(ancestor::*[2])"), STR("foo"));
 -	CHECK_XPATH_STRING(foo, STR("name(ancestor::*[1])"), STR("baz"));
 -}
 -
 -TEST_XML(xpath_xalan_axes_16, "<doc><child><grandchild><greatgrandchild/></grandchild><grandchild><greatgrandchild><greatgreatgreatgrandchild/></greatgrandchild><greatgrandchild/></grandchild></child><child><grandchild><greatgrandchild/><greatgrandchild/><greatgrandchild/></grandchild></child><child><grandchild><greatgrandchild/></grandchild></child><child><grandchild/></child><child><grandchild/><grandchild/></child><child/></doc>")
 -{
 -	xml_node c1 = doc.child(STR("doc")).child(STR("child")), c2 = c1.next_sibling(), c3 = c2.next_sibling(), c4 = c3.next_sibling(), c5 = c4.next_sibling(), c6 = c5.next_sibling();
 -
 -	CHECK_XPATH_STRING(c1.first_child(), STR("concat(count(descendant::*), ',', count(descendant-or-self::*))"), STR("1,2"));
 -	CHECK_XPATH_STRING(c1.last_child(), STR("concat(count(descendant::*), ',', count(descendant-or-self::*))"), STR("3,4"));
 -
 -	CHECK_XPATH_STRING(c2.first_child(), STR("concat(count(descendant::*), ',', count(descendant-or-self::*))"), STR("3,4"));
 -
 -	CHECK_XPATH_STRING(c3.first_child(), STR("concat(count(descendant::*), ',', count(descendant-or-self::*))"), STR("1,2"));
 -
 -	CHECK_XPATH_STRING(c4.first_child(), STR("concat(count(descendant::*), ',', count(descendant-or-self::*))"), STR("0,1"));
 -
 -	CHECK_XPATH_STRING(c5.first_child(), STR("concat(count(descendant::*), ',', count(descendant-or-self::*))"), STR("0,1"));
 -	CHECK_XPATH_STRING(c5.last_child(), STR("concat(count(descendant::*), ',', count(descendant-or-self::*))"), STR("0,1"));
 -
 -	CHECK_XPATH_STRING(c6, STR("concat(count(descendant::*), ',', count(descendant-or-self::*))"), STR("0,1"));
 -
 -	CHECK_XPATH_STRING(xml_node(), STR("concat(count(descendant::*), ',', count(descendant-or-self::*))"), STR("0,0"));
 -}
 -
 -TEST_XML(xpath_xalan_axes_17, "<doc><a><asub><asubsub><yy/></asubsub></asub></a><b><bsub><xx><xxchild/></xx></bsub></b><xx>here</xx><d><dsub><dsubsub><xx/></dsubsub></dsub></d><e><esub><xx><xxchild/></xx></esub><esubsib><sibchild/></esubsib></e><xx><childofxx/></xx><xx><xxsub><xxsubsub/></xxsub></xx></doc>")
 -{
 -	CHECK_XPATH_NODESET(doc, STR("//xx/descendant::*")) % 10 % 20 % 24 % 26 % 27;
 -}
 -
 -TEST_XML(xpath_xalan_axes_18, "<north><center center-attr='here'><south/></center></north>")
 -{
 -	xml_node center = doc.child(STR("north")).child(STR("center"));
 -
 -	CHECK_XPATH_NODESET(center, STR("@*/self::node()")) % 4;
 -	CHECK_XPATH_NODESET(center, STR("@*/self::*")); // * tests for principal node type
 -	CHECK_XPATH_NODESET(center, STR("@*/self::text()"));
 -	CHECK_XPATH_NODESET(center, STR("@*/self::center-attr")); // * tests for principal node type
 -}
 -
 -#endif
 +#ifndef PUGIXML_NO_XPATH + +#include "common.hpp" + +TEST_XML(xpath_xalan_axes_1, "<far-north><north-north-west1/><north-north-west2/><north><near-north><far-west/><west/><near-west/><center center-attr-1='c1' center-attr-2='c2' center-attr-3='c3'><near-south-west/><near-south><south><far-south/></south></near-south><near-south-east/></center><near-east/><east/><far-east/></near-north></north><north-north-east1/><north-north-east2/></far-north>") +{ +	xml_node center = doc.select_single_node(STR("//center")).node(); + +	CHECK_XPATH_NODESET(center, STR("self::*[near-south]")) % 10; +	CHECK_XPATH_NODESET(center, STR("self::*[@center-attr-2]")) % 10; +	CHECK_XPATH_NODESET(center, STR("preceding-sibling::*")) % 9 % 8 % 7; +	CHECK_XPATH_NODESET(center, STR("preceding-sibling::*/following-sibling::*")) % 8 % 9 % 10 % 19 % 20 % 21; +	CHECK_XPATH_NODESET(center, STR("preceding-sibling::*[2]/following-sibling::*")) % 9 % 10 % 19 % 20 % 21; +	CHECK_XPATH_NODESET(center, STR("preceding-sibling::*[2]/following-sibling::*[4]")) % 20; +	CHECK_XPATH_NODESET(center, STR("preceding-sibling::*[2]/following-sibling::*[4]/preceding-sibling::*[5]/following-sibling::*[4]/following-sibling::*[2]")) % 21; +	CHECK_XPATH_NODESET(center, STR("following-sibling::*")) % 19 % 20 % 21; +	CHECK_XPATH_NODESET(center, STR("following-sibling::*/preceding-sibling::*")) % 7 % 8 % 9 % 10 % 19 % 20; +	CHECK_XPATH_NODESET(center, STR("following-sibling::*[2]/preceding-sibling::*")) % 19 % 10 % 9 % 8 % 7; +	CHECK_XPATH_NODESET(center, STR("following-sibling::*[2]/preceding-sibling::*[4]")) % 8; +	CHECK_XPATH_NODESET(center, STR("following-sibling::*[2]/preceding-sibling::*[4]/following-sibling::*[5]/preceding-sibling::*[4]/preceding-sibling::*[2]")) % 7; +	CHECK_XPATH_NODESET(center, STR("following::*[4]/../*[2]")) % 4; +	CHECK_XPATH_NODESET(center, STR("preceding::*[2]/../following::*")) % 22 % 23; +	CHECK_XPATH_NODESET(center, STR("preceding::*[2]/../descendant::*[10]/following-sibling::east")) % 20; +	CHECK_XPATH_NODESET(center, STR("//*")) % 2 % 3 % 4 % 5 % 6 % 7 % 8 % 9 % 10 % 14 % 15 % 16 % 17 % 18 % 19 % 20 % 21 % 22 % 23; +	CHECK_XPATH_NODESET(center, STR("//ancestor::*")) % 2 % 5 % 6 % 10 % 15 % 16; +	CHECK_XPATH_NODESET(center, STR("//*[count(ancestor::*) >= 2]/../parent::*")) % 2 % 5 % 6 % 10 % 15; +	CHECK_XPATH_NODESET(center, STR("//*[count(./*/*) > 0]")) % 2 % 5 % 6 % 10 % 15; +	CHECK_XPATH_NODESET(center, STR("@*/ancestor::*")) % 2 % 5 % 6 % 10; +	CHECK_XPATH_NODESET(center, STR("@*/following::*")) % 14 % 15 % 16 % 17 % 18 % 19 % 20 % 21 % 22 % 23; +	CHECK_XPATH_NODESET(center, STR("@*/preceding::*")) % 3 % 4 % 7 % 8 % 9; +	CHECK_XPATH_NODESET(center, STR("preceding-sibling::*|following-sibling::*")) % 7 % 8 % 9 % 19 % 20 % 21; +	CHECK_XPATH_NODESET(center, STR("(preceding-sibling::*|following-sibling::*)/ancestor::*[last()]/*[last()]")) % 23; +	CHECK_XPATH_NODESET(center, STR(".//near-south/preceding-sibling::*|following-sibling::east/ancestor-or-self::*[2]")) % 6 % 14; +} + +TEST_XML_FLAGS(xpath_xalan_axes_2, "<far-north> Level-1<north-north-west1/><north-north-west2/><!-- Comment-2 --> Level-2<?a-pi pi-2?><north><!-- Comment-3 --> Level-3<?a-pi pi-3?><near-north><far-west/><west/><near-west/><!-- Comment-4 --> Level-4<?a-pi pi-4?><center center-attr-1='c1' center-attr-2='c2' center-attr-3='c3'><near-south-west/><!--Comment-5-->   Level-5<?a-pi pi-5?><near-south><!--Comment-6-->   Level-6<?a-pi pi-6?><south attr1='First' attr2='Last'> <far-south/></south></near-south><near-south-east/></center><near-east/><east/><far-east/></near-north></north><north-north-east1/><north-north-east2/></far-north>", parse_default | parse_comments | parse_pi) +{ +	xml_node center = doc.select_single_node(STR("//center")).node(); + +	CHECK_XPATH_NODESET(center, STR("@*")) % 21 % 22 % 23; +	CHECK_XPATH_NODESET(center, STR("@*/child::*")); +	CHECK_XPATH_NODESET(center, STR("@*/descendant::node()")); +	CHECK_XPATH_NODESET(center, STR("@*/parent::node()")) % 20; +	CHECK_XPATH_NODESET(center, STR("@*/ancestor::node()")) % 1 % 2 % 9 % 13 % 20; +	CHECK_XPATH_NODESET(center, STR("@*/self::node()")) % 21 % 22 % 23; +	CHECK_XPATH_NODESET(center, STR("@*/.")) % 21 % 22 % 23; +	CHECK_XPATH_NODESET(center, STR("@*/descendant-or-self::node()")) % 21 % 22 % 23; +	CHECK_XPATH_NODESET(center, STR("@*/ancestor-or-self::node()")) % 1 % 2 % 9 % 13 % 20 % 21 % 22 % 23; +	CHECK_XPATH_NODESET(center, STR("@*/ancestor-or-self::*")) % 2 % 9 % 13 % 20; +	CHECK_XPATH_NODESET(center, STR("@*/preceding-sibling::node()")); +	CHECK_XPATH_NODESET(center, STR("@*/following-sibling::*")); +	CHECK_XPATH_NODESET(center, STR("@*/ancestor::*/near-north/*[4]/@*/preceding::*")) % 4 % 5 % 14 % 15 % 16; +	CHECK_XPATH_NODESET(center, STR("@*/ancestor::*/near-north/*[4]/@*/preceding::comment()")) % 6 % 10 % 17; +	CHECK_XPATH_NODESET(center, STR("@*/ancestor::*/near-north/*[4]/@*/preceding::text()")) % 3 % 7 % 11 % 18; +	CHECK_XPATH_NODESET(center, STR("@*/ancestor::*/near-north/*[4]/@*/preceding::processing-instruction()")) % 8 % 12 % 19; +	CHECK_XPATH_NODESET(center, STR("@*/following::comment()")) % 25 % 29; +	CHECK_XPATH_NODESET(center, STR("@*/following::processing-instruction()")) % 27 % 31; +	CHECK_XPATH_NODESET(center, STR("@*/following::text()")) % 26 % 30; +	CHECK_XPATH_NODESET(center, STR("@*/ancestor::*/near-north/*[4]/@*/preceding::node()")) % 3 % 4 % 5 % 6 % 7 % 8 % 10 % 11 % 12 % 14 % 15 % 16 % 17 % 18 % 19; +	CHECK_XPATH_NODESET(center, STR("@*/ancestor::*/near-north/*[4]/@*/following::node()")) % 24 % 25 % 26 % 27 % 28 % 29 % 30 % 31 % 32 % 35 % 36 % 37 % 38 % 39 % 40 % 41; + +	CHECK_XPATH_NODESET(center, STR("(//comment())[1]/..")) % 2; +	CHECK_XPATH_NODESET(center, STR("(//attribute::*)[1]/../..")) % 13; +} + +TEST_XML(xpath_xalan_axes_3, "<far-north><north><near-north><far-west/><west/><near-west/><center><near-south><south><far-south/></south></near-south></center><near-east/><east/><far-east/></near-north></north></far-north>") +{ +	xml_node center = doc.select_single_node(STR("//center")).node(); + +	CHECK_XPATH_NODESET(center, STR("ancestor-or-self::*")) % 8 % 4 % 3 % 2; +	CHECK_XPATH_NODESET(center, STR("ancestor::*[3]")) % 2; +	CHECK_XPATH_NODESET(center, STR("ancestor-or-self::*[1]")) % 8; +	CHECK_XPATH_NODESET(center, STR("@*[2]")); +	CHECK_XPATH_NODESET(center, STR("child::*[2]")); +	CHECK_XPATH_NODESET(center, STR("child::near-south-west")); +	CHECK_XPATH_NODESET(center, STR("descendant::*[3]")) % 11; +	CHECK_XPATH_NODESET(center, STR("descendant::far-south")) % 11; +	CHECK_XPATH_NODESET(center, STR("descendant-or-self::*[3]")) % 10; +	CHECK_XPATH_NODESET(center, STR("descendant-or-self::far-south")) % 11; +	CHECK_XPATH_NODESET(center, STR("descendant-or-self::center")) % 8; +	CHECK_XPATH_NODESET(center, STR("following::*[4]")); +	CHECK_XPATH_NODESET(center, STR("following::out-yonder-east")); +	CHECK_XPATH_NODESET(center, STR("preceding::*[4]")); +	CHECK_XPATH_NODESET(center, STR("preceding::out-yonder-west")); +	CHECK_XPATH_NODESET(center, STR("following-sibling::*[2]")) % 13; +	CHECK_XPATH_NODESET(center, STR("following-sibling::east")) % 13; +	CHECK_XPATH_NODESET(center, STR("preceding-sibling::*[2]")) % 6; +	CHECK_XPATH_NODESET(center, STR("preceding-sibling::west")) % 6; +	CHECK_XPATH_NODESET(center, STR("parent::near-north")) % 4; +	CHECK_XPATH_NODESET(center, STR("parent::*[1]")) % 4; +	CHECK_XPATH_NODESET(center, STR("parent::foo")); +	CHECK_XPATH_NODESET(center, STR("..")) % 4; +	CHECK_XPATH_NODESET(center, STR("self::center")) % 8; +	CHECK_XPATH_NODESET(center, STR("self::*[1]")) % 8; +	CHECK_XPATH_NODESET(center, STR("self::foo")); +	CHECK_XPATH_NODESET(center, STR(".")) % 8; +	CHECK_XPATH_NODESET(center, STR("/far-north/north/near-north/center/ancestor-or-self::*")) % 8 % 4 % 3 % 2; +} + +TEST_XML(xpath_xalan_axes_4, "<far-north><north><near-north><far-west/><west/><near-west/><center><near-south><south><far-south/></south></near-south></center><near-east/><east/><far-east/></near-north></north></far-north>") +{ +	xml_node north = doc.select_single_node(STR("//north")).node(); + +	CHECK_XPATH_STRING(north, STR("name(/descendant-or-self::north)"), STR("north")); +	CHECK_XPATH_STRING(north, STR("name(/descendant::near-north)"), STR("near-north")); +	CHECK_XPATH_STRING(north, STR("name(self::node()/descendant-or-self::north)"), STR("north")); +	CHECK_XPATH_STRING(north, STR("name(self::node()/descendant::near-north)"), STR("near-north")); +	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::north/descendant-or-self::north)"), STR("north")); +	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::north/descendant::near-north)"), STR("near-north")); +	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::north/child::near-north)"), STR("near-north")); +	CHECK_XPATH_STRING(north, STR("name(descendant::near-north/descendant-or-self::near-north)"), STR("near-north")); +	CHECK_XPATH_STRING(north, STR("name(descendant::near-north/descendant::far-west)"), STR("far-west")); + +	CHECK_XPATH_STRING(north, STR("name(/descendant-or-self::north/descendant-or-self::north)"), STR("north")); +	CHECK_XPATH_STRING(north, STR("name(/descendant-or-self::north/child::near-north)"), STR("near-north")); +	CHECK_XPATH_STRING(north, STR("name(/descendant::near-north/descendant-or-self::near-north)"), STR("near-north")); +	CHECK_XPATH_STRING(north, STR("name(self::node()/descendant-or-self::north/descendant-or-self::north)"), STR("north")); +	CHECK_XPATH_STRING(north, STR("name(self::node()/descendant-or-self::north/child::near-north)"), STR("near-north")); +	CHECK_XPATH_STRING(north, STR("name(self::node()/descendant::near-north/descendant-or-self::far-west)"), STR("far-west")); +	CHECK_XPATH_STRING(north, STR("name(self::node()/descendant::near-north/child::far-west)"), STR("far-west")); +	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::north/descendant-or-self::north/descendant-or-self::north)"), STR("north")); +	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::north/descendant-or-self::north/child::near-north)"), STR("near-north")); +	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::north/descendant::near-north/descendant-or-self::near-north)"), STR("near-north")); +	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::north/descendant::near-north/child::far-west)"), STR("far-west")); +	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::north/child::near-north/descendant-or-self::near-north)"), STR("near-north")); +	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::north/child::near-north/child::far-west)"), STR("far-west")); +	CHECK_XPATH_STRING(north, STR("name(descendant::near-north/descendant-or-self::near-north/descendant-or-self::near-north)"), STR("near-north")); +	CHECK_XPATH_STRING(north, STR("name(descendant::near-north/descendant-or-self::near-north/child::far-west)"), STR("far-west")); +	CHECK_XPATH_STRING(north, STR("name(descendant::near-north/descendant::far-west/descendant-or-self::far-west)"), STR("far-west")); + +	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::node()/descendant-or-self::north)"), STR("north")); +	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::node()/descendant::near-north)"), STR("near-north")); +	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::node()/child::near-north)"), STR("near-north")); +	CHECK_XPATH_STRING(north, STR("name(descendant::node()/descendant-or-self::near-north)"), STR("near-north")); +	CHECK_XPATH_STRING(north, STR("name(descendant::node()/descendant::far-west)"), STR("far-west")); + +	CHECK_XPATH_STRING(north, STR("name(/descendant-or-self::node()/descendant-or-self::north)"), STR("north")); +	CHECK_XPATH_STRING(north, STR("name(/descendant-or-self::node()/child::near-north)"), STR("near-north")); +	CHECK_XPATH_STRING(north, STR("name(/descendant::node()/descendant-or-self::near-north)"), STR("near-north")); +	CHECK_XPATH_STRING(north, STR("name(self::node()/descendant-or-self::node()/descendant-or-self::north)"), STR("north")); +	CHECK_XPATH_STRING(north, STR("name(self::node()/descendant-or-self::node()/child::near-north)"), STR("near-north")); +	CHECK_XPATH_STRING(north, STR("name(self::node()/descendant::node()/descendant-or-self::far-west)"), STR("far-west")); +	CHECK_XPATH_STRING(north, STR("name(self::node()/descendant::node()/child::far-west)"), STR("far-west")); +	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::node()/descendant-or-self::node()/descendant-or-self::north)"), STR("north")); +	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::node()/descendant-or-self::node()/child::near-north)"), STR("near-north")); +	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::node()/descendant::node()/descendant-or-self::near-north)"), STR("near-north")); +	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::node()/descendant::node()/child::far-west)"), STR("far-west")); +	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::node()/child::node()/descendant-or-self::near-north)"), STR("near-north")); +	CHECK_XPATH_STRING(north, STR("name(descendant-or-self::node()/child::node()/child::far-west)"), STR("far-west")); +	CHECK_XPATH_STRING(north, STR("name(descendant::node()/descendant-or-self::node()/descendant-or-self::near-north)"), STR("near-north")); +	CHECK_XPATH_STRING(north, STR("name(descendant::node()/descendant-or-self::node()/child::far-west)"), STR("far-west")); +	CHECK_XPATH_STRING(north, STR("name(descendant::node()/descendant::node()/descendant-or-self::far-west)"), STR("far-west")); +} + +TEST_XML_FLAGS(xpath_xalan_axes_5, "<text>text</text><comment><!--comment--></comment><pi><?pi?></pi>", parse_default | parse_comments | parse_pi) +{ +	CHECK_XPATH_NODESET(doc, STR("text/self::text()")); +	CHECK_XPATH_NODESET(doc, STR("comment/self::comment()")); +	CHECK_XPATH_NODESET(doc, STR("pi/self::processing-instruction()")); +} + +TEST_XML(xpath_xalan_axes_6, "<doc><T>Test for source tree depth</T><a><T>A</T><b><T>B</T><c><T>C</T><d><T>D</T><e><T>E</T><f><T>F</T><g><T>G</T><h><T>H</T><i><T>I</T><j><T>J</T><k><T>K</T><l><T>L</T><m><T>M</T><n><T>N</T><o><T>O</T></o></n></m></l></k></j></i></h></g></f></e></d></c></b></a></doc>") +{ +	CHECK_XPATH_NODESET(doc, STR("//T")) % 3 % 6 % 9 % 12 % 15 % 18 % 21 % 24 % 27 % 30 % 33 % 36 % 39 % 42 % 45 % 48; +} + +TEST_XML(xpath_xalan_axes_7, "<far-north><north><near-north><far-west/><west/><near-west/><center center-attr-1='c1' center-attr-2='c2' center-attr-3='c3'><near-south><south><far-south/></south></near-south></center><near-east/><east/><far-east/></near-north></north></far-north>") +{ +	xml_node center = doc.select_single_node(STR("//center")).node(); + +	CHECK_XPATH_NODESET(center, STR("attribute::*[2]")) % 10; +	CHECK_XPATH_NODESET(center, STR("@*")) % 9 % 10 % 11; +	CHECK_XPATH_NODESET(center, STR("child::*/child::*")) % 13; +	CHECK_XPATH_NODESET(center, STR("child::*/descendant::*")) % 13 % 14; +	CHECK_XPATH_NODESET(center, STR("descendant::*/child::*")) % 13 % 14; +} + +TEST_XML(xpath_xalan_axes_8, "<far-north><north><near-north><far-west/><west/><near-west/><center center-attr-1='c1' center-attr-2='c2' center-attr-3='c3'><near-south-east/><near-south><south><far-south/></south></near-south><near-south-west/></center><near-east/><east/><far-east/></near-north></north></far-north>") +{ +	xml_node near_north = doc.select_single_node(STR("//near-north")).node(); + +	CHECK_XPATH_NODESET(near_north, STR("center//child::*")) % 12 % 13 % 14 % 15 % 16; +	CHECK_XPATH_NODESET(near_north, STR("center//descendant::*")) % 12 % 13 % 14 % 15 % 16; +	CHECK_XPATH_NODESET(near_north, STR("center/descendant::*")) % 12 % 13 % 14 % 15 % 16; +	CHECK_XPATH_NODESET(near_north, STR("center/child::*")) % 12 % 13 % 16; +	CHECK_XPATH_NODESET(near_north, STR("center//*")) % 12 % 13 % 14 % 15 % 16; +} + +TEST_XML(xpath_xalan_axes_9, "<doc><foo att1='c'><foo att1='b'><foo att1='a'><baz/></foo></foo></foo><bar/></doc>") +{ +	xml_node baz = doc.select_single_node(STR("//baz")).node(); + +	CHECK_XPATH_NODESET(baz, STR("ancestor-or-self::*[@att1][1]/@att1")) % 8; +    CHECK_XPATH_NODESET(baz, STR("(ancestor-or-self::*)[@att1][1]/@att1")) % 4; + +	CHECK_XPATH_NODESET(baz, STR("ancestor::foo[1]/@att1")) % 8; +	CHECK_XPATH_NODESET(baz, STR("(ancestor::foo[1])/@att1")) % 8; +	CHECK_XPATH_NODESET(baz, STR("(ancestor::foo)[1]/@att1")) % 4; +	CHECK_XPATH_NODESET(baz, STR("((ancestor::foo))[1]/@att1")) % 4; +	CHECK_XPATH_NODESET(baz, STR("(((ancestor::foo)[1])/@att1)")) % 4; + +	xml_node bar = doc.child(STR("doc")).child(STR("bar")); + +    CHECK_XPATH_NODESET(bar, STR("preceding::foo[1]/@att1")) % 8; +	CHECK_XPATH_NODESET(bar, STR("(preceding::foo)[1]/@att1")) % 4; +} + +TEST_XML(xpath_xalan_axes_10, "<doc><foo att1='c'/><foo att1='b'/><foo att1='a'/><baz/></doc>") +{ +	xml_node baz = doc.child(STR("doc")).child(STR("baz")); + +    CHECK_XPATH_NODESET(baz, STR("preceding-sibling::foo[1]/@att1")) % 8; +	CHECK_XPATH_NODESET(baz, STR("(preceding-sibling::foo)[1]/@att1")) % 4; +} + +TEST_XML(xpath_xalan_axes_11, "<chapter title='A' x='0'><section title='A1' x='1'><subsection title='A1a' x='2'>hello</subsection><subsection title='A1b'>ahoy</subsection></section><section title='A2'><subsection title='A2a'>goodbye</subsection><subsection title='A2b'>sayonara</subsection><subsection title='A2c'>adios</subsection></section><section title='A3'><subsection title='A3a'>aloha</subsection><subsection title='A3b'><footnote x='3'>A3b-1</footnote><footnote>A3b-2</footnote></subsection><subsection title='A3c'>shalom</subsection></section></chapter>") +{ +	xml_node chapter = doc.child(STR("chapter")); + +    CHECK_XPATH_NUMBER(doc, STR("count(//@*)"), 16); +    CHECK_XPATH_NUMBER(doc, STR("count(//@title)"), 12); +    CHECK_XPATH_NUMBER(doc, STR("count(//section//@*)"), 14); +    CHECK_XPATH_NUMBER(doc, STR("count(//section//@title)"), 11); + +	CHECK_XPATH_NUMBER(chapter, STR("count(.//@*)"), 16); +	CHECK_XPATH_NUMBER(chapter, STR("count(.//@title)"), 12); +	CHECK_XPATH_NUMBER(chapter, STR("count(section[1]//@*)"), 5); +	CHECK_XPATH_NUMBER(chapter, STR("count(section[1]//@title)"), 3); +	CHECK_XPATH_NUMBER(chapter, STR("count(section[2]//@*)"), 4); +	CHECK_XPATH_NUMBER(chapter, STR("count(section[2]//@title)"), 4); +	CHECK_XPATH_NUMBER(chapter, STR("count(section[3]//@*)"), 5); +	CHECK_XPATH_NUMBER(chapter, STR("count(section[3]//@title)"), 4); +} + +TEST_XML_FLAGS(xpath_xalan_axes_12, "<far-north><north>north-text1<near-north><far-west/><west><!-- Western comment --></west><near-west/><center>center-text1<near-south><south>south-text</south></near-south><near-south-west/>center-text2</center><near-east/><east><!-- Eastern comment --></east><far-east/></near-north>north-text2</north></far-north>", parse_default | parse_comments) +{ +	CHECK_XPATH_NODESET(doc, STR("/descendant::*")) % 2 % 3 % 5 % 6 % 7 % 9 % 10 % 12 % 13 % 15 % 17 % 18 % 20; +	CHECK_XPATH_NODESET(doc, STR("far-north/..//*")) % 2 % 3 % 5 % 6 % 7 % 9 % 10 % 12 % 13 % 15 % 17 % 18 % 20; +	CHECK_XPATH_NODESET(doc, STR("far-north/north/..//*")) % 3 % 5 % 6 % 7 % 9 % 10 % 12 % 13 % 15 % 17 % 18 % 20; +	CHECK_XPATH_NODESET(doc, STR("far-north/north-yonder/..//*")); +} + +TEST_XML(xpath_xalan_axes_13, "<doc att1='e'><foo att1='d'><foo att1='c'><foo att1='b'><baz att1='a'/></foo></foo></foo></doc>") +{ +	xml_node d = doc.child(STR("doc")); +	xml_node baz = doc.select_single_node(STR("//baz")).node(); + +	CHECK_XPATH_NUMBER(d, STR("count(descendant-or-self::*/@att1)"), 5); +	CHECK_XPATH_NODESET(d, STR("descendant-or-self::*/@att1[last()]")) % 3 % 5 % 7 % 9 % 11; +	CHECK_XPATH_STRING(d, STR("string(descendant-or-self::*/@att1[last()])"), STR("e")); +	CHECK_XPATH_NODESET(d, STR("descendant-or-self::*[last()]/@att1")) % 11; +	CHECK_XPATH_NODESET(d, STR("(descendant-or-self::*/@att1)[last()]")) % 11; + +	CHECK_XPATH_NUMBER(baz, STR("count(ancestor-or-self::*/@att1)"), 5); +    CHECK_XPATH_NODESET(baz, STR("ancestor-or-self::*/@att1[last()]")) % 3 % 5 % 7 % 9 % 11; +    CHECK_XPATH_STRING(baz, STR("string(ancestor-or-self::*/@att1[last()])"), STR("e")); +    CHECK_XPATH_NODESET(baz, STR("(ancestor-or-self::*)/@att1[last()]")) % 3 % 5 % 7 % 9 % 11; +    CHECK_XPATH_STRING(baz, STR("string((ancestor-or-self::*)/@att1[last()])"), STR("e")); +    CHECK_XPATH_NODESET(baz, STR("(ancestor-or-self::*/@att1)[last()]")) % 11; +    CHECK_XPATH_NODESET(baz, STR("(ancestor::*|self::*)/@att1[last()]")) % 3 % 5 % 7 % 9 % 11; +    CHECK_XPATH_STRING(baz, STR("string((ancestor::*|self::*)/@att1[last()])"), STR("e")); +	CHECK_XPATH_NODESET(baz, STR("((ancestor::*|self::*)/@att1)[last()]")) % 11; +} + +TEST_XML_FLAGS(xpath_xalan_axes_14, "<doc><n a='v'/><?pi?><!--comment-->text<center/>text<!--comment--><?pi?><n a='v'/></doc>", parse_default | parse_comments | parse_pi) +{ +	CHECK_XPATH_NODESET(doc, STR("//center/preceding::node()")) % 7 % 6 % 5 % 3; +	CHECK_XPATH_NODESET(doc, STR("//center/following::node()")) % 9 % 10 % 11 % 12; +} + +TEST_XML(xpath_xalan_axes_15, "<doc><foo new='true'><baz>is new</baz></foo><foo new='true'>xyz<baz>is new but has text</baz></foo><foo new='false'><baz>is not new</baz></foo></doc>") +{ +	CHECK_XPATH_NODESET(doc, STR("//text()[ancestor::*[@new='true'][not(text())]]")) % 6; +	CHECK_XPATH_NODESET(doc, STR("//text()[ancestor::*[2][@new]]")) % 6 % 11 % 15; + +	xml_node foo = doc.child(STR("doc")).child(STR("foo")).child(STR("baz")).first_child(); + +	CHECK_XPATH_STRING(foo, STR("name(ancestor::*[3])"), STR("doc")); +	CHECK_XPATH_STRING(foo, STR("name(ancestor::*[2])"), STR("foo")); +	CHECK_XPATH_STRING(foo, STR("name(ancestor::*[1])"), STR("baz")); +} + +TEST_XML(xpath_xalan_axes_16, "<doc><child><grandchild><greatgrandchild/></grandchild><grandchild><greatgrandchild><greatgreatgreatgrandchild/></greatgrandchild><greatgrandchild/></grandchild></child><child><grandchild><greatgrandchild/><greatgrandchild/><greatgrandchild/></grandchild></child><child><grandchild><greatgrandchild/></grandchild></child><child><grandchild/></child><child><grandchild/><grandchild/></child><child/></doc>") +{ +	xml_node c1 = doc.child(STR("doc")).child(STR("child")), c2 = c1.next_sibling(), c3 = c2.next_sibling(), c4 = c3.next_sibling(), c5 = c4.next_sibling(), c6 = c5.next_sibling(); + +	CHECK_XPATH_STRING(c1.first_child(), STR("concat(count(descendant::*), ',', count(descendant-or-self::*))"), STR("1,2")); +	CHECK_XPATH_STRING(c1.last_child(), STR("concat(count(descendant::*), ',', count(descendant-or-self::*))"), STR("3,4")); + +	CHECK_XPATH_STRING(c2.first_child(), STR("concat(count(descendant::*), ',', count(descendant-or-self::*))"), STR("3,4")); + +	CHECK_XPATH_STRING(c3.first_child(), STR("concat(count(descendant::*), ',', count(descendant-or-self::*))"), STR("1,2")); + +	CHECK_XPATH_STRING(c4.first_child(), STR("concat(count(descendant::*), ',', count(descendant-or-self::*))"), STR("0,1")); + +	CHECK_XPATH_STRING(c5.first_child(), STR("concat(count(descendant::*), ',', count(descendant-or-self::*))"), STR("0,1")); +	CHECK_XPATH_STRING(c5.last_child(), STR("concat(count(descendant::*), ',', count(descendant-or-self::*))"), STR("0,1")); + +	CHECK_XPATH_STRING(c6, STR("concat(count(descendant::*), ',', count(descendant-or-self::*))"), STR("0,1")); + +	CHECK_XPATH_STRING(xml_node(), STR("concat(count(descendant::*), ',', count(descendant-or-self::*))"), STR("0,0")); +} + +TEST_XML(xpath_xalan_axes_17, "<doc><a><asub><asubsub><yy/></asubsub></asub></a><b><bsub><xx><xxchild/></xx></bsub></b><xx>here</xx><d><dsub><dsubsub><xx/></dsubsub></dsub></d><e><esub><xx><xxchild/></xx></esub><esubsib><sibchild/></esubsib></e><xx><childofxx/></xx><xx><xxsub><xxsubsub/></xxsub></xx></doc>") +{ +	CHECK_XPATH_NODESET(doc, STR("//xx/descendant::*")) % 10 % 20 % 24 % 26 % 27; +} + +TEST_XML(xpath_xalan_axes_18, "<north><center center-attr='here'><south/></center></north>") +{ +	xml_node center = doc.child(STR("north")).child(STR("center")); + +	CHECK_XPATH_NODESET(center, STR("@*/self::node()")) % 4; +	CHECK_XPATH_NODESET(center, STR("@*/self::*")); // * tests for principal node type +	CHECK_XPATH_NODESET(center, STR("@*/self::text()")); +	CHECK_XPATH_NODESET(center, STR("@*/self::center-attr")); // * tests for principal node type +} + +#endif diff --git a/tests/test_xpath_xalan_4.cpp b/tests/test_xpath_xalan_4.cpp index 10784da..c71eaf7 100644 --- a/tests/test_xpath_xalan_4.cpp +++ b/tests/test_xpath_xalan_4.cpp @@ -1,298 +1,298 @@ -#ifndef PUGIXML_NO_XPATH
 -
 -#include "common.hpp"
 -
 -TEST_XML(xpath_xalan_position_1, "<doc><a>1</a><a>2</a><a>3</a><a>4</a></doc>")
 -{
 -	xml_node c = doc.child(STR("doc"));
 -
 -	CHECK_XPATH_BOOLEAN(c, STR("position()=1"), true);
 -	CHECK_XPATH_NODESET(c, STR("*[position()=4]")) % 9;
 -}
 -
 -TEST_XML_FLAGS(xpath_xalan_position_2, "<doc><a test='true'><num>1</num></a><a><num>1191</num></a><a><num>263</num></a><a test='true'><num>2</num></a><a><num>827</num></a><a><num>256</num></a><a test='true'><num>3</num></a><a test='true'><num>4<x/>5</num></a><?pi?><?pi?><!--comment--><!--comment--></doc>", parse_default | parse_comments | parse_pi)
 -{
 -	xml_node c = doc.child(STR("doc"));
 -
 -	CHECK_XPATH_NODESET(c, STR("*[@test and position()=8]")) % 27;
 -	CHECK_XPATH_NODESET(c, STR("*[@test][position()=4]/num")) % 29;
 -	CHECK_XPATH_NUMBER(c, STR("count(*)"), 8);
 -	CHECK_XPATH_NODESET(c, STR("*[last()=position()]")) % 27;
 -	CHECK_XPATH_NODESET(c, STR("a[position()=2]")) % 7;
 -	CHECK_XPATH_NODESET(c, STR("a[3]/../a[position()=4]/num/../@test")) % 14;
 -	CHECK_XPATH_BOOLEAN(c, STR("not(position()=last())"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("position()=2"), false);
 -	CHECK_XPATH_BOOLEAN(c, STR("last()=1"), true);
 -	CHECK_XPATH_BOOLEAN(c, STR("last()+2=3"), true);
 -	CHECK_XPATH_NODESET(c, STR("a[position()=5 mod 3]")) % 7;
 -	CHECK_XPATH_NODESET(c, STR("a/num/text()[position()=1]")) % 6 % 9 % 12 % 16 % 19 % 22 % 26 % 30;
 -	CHECK_XPATH_NODESET(c, STR("a/num/text()[position()=2]")) % 32;
 -	CHECK_XPATH_NODESET(c, STR("a/num/text()[position()=last()]")) % 6 % 9 % 12 % 16 % 19 % 22 % 26 % 32;
 -	CHECK_XPATH_NODESET(c, STR("a/num/text()[1]")) % 6 % 9 % 12 % 16 % 19 % 22 % 26 % 30;
 -	CHECK_XPATH_NODESET(c, STR("a/num/text()[2]")) % 32;
 -	CHECK_XPATH_NODESET(c, STR("a/num/text()[last()]")) % 6 % 9 % 12 % 16 % 19 % 22 % 26 % 32;
 -	CHECK_XPATH_NODESET(c, STR("a[floor(last() div 3)]")) % 7;
 -	CHECK_XPATH_NODESET(c, STR("a[ceiling(last() div 3)]")) % 10;
 -	CHECK_XPATH_NODESET(c, STR("a[round(last() div 3)]")) % 10;
 -	CHECK_XPATH_NODESET(c, STR("a[last() div 3]"));
 -	CHECK_XPATH_NODESET(c, STR("a[last() div 2]")) % 13;
 -	CHECK_XPATH_NODESET(c, STR("a[3]/../a[position()>=2 and position()<=4]")) % 7 % 10 % 13;
 -	CHECK_XPATH_NUMBER(c, STR("count(a[position()>=2 and position()<=4]/num)"), 3);
 -	CHECK_XPATH_NUMBER(c, STR("count(a/@*)"), 4);
 -	CHECK_XPATH_NUMBER(c, STR("count(a/attribute::*)"), 4);
 -	CHECK_XPATH_NODESET(c, STR("*[not(@test)][position()=last()]")) % 20;
 -	CHECK_XPATH_NODESET(c, STR("*[not(@test)][last()]")) % 20;
 -	CHECK_XPATH_NODESET(c, STR("a[3-2]")) % 3;
 -	CHECK_XPATH_NODESET(c, STR("a[0]"));
 -	CHECK_XPATH_NODESET(c, STR("a[9]"));
 -	CHECK_XPATH_NODESET(c, STR("a['3']")) % 3 % 7 % 10 % 13 % 17 % 20 % 23 % 27;
 -	CHECK_XPATH_NODESET(c, STR("a[number('3')]")) % 10;
 -	CHECK_XPATH_NODESET(c, STR("processing-instruction()[2]")) % 34;
 -	CHECK_XPATH_NODESET(c, STR("processing-instruction('pi')[2]")) % 34;
 -	CHECK_XPATH_NODESET(c, STR("comment()[2]")) % 36;
 -    CHECK_XPATH_NODESET(c, STR("a/*[last()]")) % 5 % 8 % 11 % 15 % 18 % 21 % 25 % 29;
 -    CHECK_XPATH_NODESET(c, STR("a/child::*[last()]")) % 5 % 8 % 11 % 15 % 18 % 21 % 25 % 29;
 -    CHECK_XPATH_NODESET(c, STR("a/descendant::*[last()]")) % 5 % 8 % 11 % 15 % 18 % 21 % 25 % 31;
 -    CHECK_XPATH_NODESET(c, STR("a/child::node()[last()]")) % 5 % 8 % 11 % 15 % 18 % 21 % 25 % 29;
 -    CHECK_XPATH_NODESET(c, STR("a/descendant::text()[last()]")) % 6 % 9 % 12 % 16 % 19 % 22 % 26 % 32;
 -    CHECK_XPATH_NODESET(c, STR("child::comment()[last()]")) % 36;
 -}
 -
 -TEST_XML(xpath_xalan_position_3, "<article class='whitepaper' status='Note'><articleinfo><title>AAA</title><section id='info'><title>BBB</title><para>About this article</para><section revisionflag='added'><title>CCC</title><para>This is the section titled 'ZZZ'.</para><ednote who='KKK'><title>DDD</title><para>Don't worry.</para><section revisionflag='added'><title>EEE</title><para>This is the deep subsection.</para></section></ednote></section></section></articleinfo></article>")
 -{
 -	CHECK_XPATH_NODESET(doc, STR("(article//section/title|/articleinfo/title|article/section/para)[last()]")) % 28;
 -	CHECK_XPATH_NODESET(doc, STR("(article//section/title|/articleinfo/title|article/section/para)[1]")) % 10;
 -	CHECK_XPATH_NUMBER(doc, STR("count(article/articleinfo/section[last()])"), 1);
 -	CHECK_XPATH_NUMBER(doc, STR("count(article/articleinfo/section[last()][title='BBB'])"), 1);
 -}
 -
 -TEST_XML(xpath_xalan_position_4, "<chapter><section><footnote>hello</footnote></section><section><footnote>goodbye</footnote><footnote>sayonara</footnote></section><section><footnote>aloha</footnote></section></chapter>")
 -{
 -	CHECK_XPATH_NODESET(doc, STR("chapter//footnote[1]")) % 4 % 7 % 12;
 -}
 -
 -TEST_XML(xpath_xalan_position_5, "<chapter><section><footnote>hello</footnote><footnote>ahoy</footnote></section><section><footnote>goodbye</footnote><footnote>sayonara</footnote><footnote>adios</footnote></section><section><footnote>aloha</footnote><subsection><footnote>shalom</footnote><footnote>yo</footnote></subsection><footnote>ciao</footnote></section></chapter>")
 -{
 -	CHECK_XPATH_NODESET(doc, STR("chapter//footnote[2]")) % 6 % 11 % 21 % 23;
 -	CHECK_XPATH_NODESET(doc, STR("(chapter//footnote)[2]")) % 6;
 -	CHECK_XPATH_NODESET(doc, STR("(child::chapter/descendant-or-self::node())/footnote[2]")) % 6 % 11 % 21 % 23;
 -	CHECK_XPATH_NODESET(doc, STR("chapter/descendant::footnote[6]")) % 16;
 -	CHECK_XPATH_NODESET(doc, STR("chapter/descendant::footnote[6][1][last()]")) % 16;
 -}
 -
 -TEST_XML_FLAGS(xpath_xalan_position_6, "<node attr='value'>pcdata<child/><?pi1 value?><?pi2 value?><!--comment--><![CDATA[cdata]]></node>", parse_default | parse_pi | parse_comments)
 -{
 -	CHECK_XPATH_NUMBER(doc, STR("count(/node/@attr/ancestor-or-self::node())"), 3);
 -	CHECK_XPATH_NUMBER(doc, STR("count(/node/text()/ancestor-or-self::node())"), 4);
 -	CHECK_XPATH_NUMBER(doc, STR("count(/node/processing-instruction()/ancestor-or-self::node())"), 4);
 -	CHECK_XPATH_NUMBER(doc, STR("count(/node/processing-instruction('pi1')/ancestor-or-self::node())"), 3);
 -	CHECK_XPATH_NUMBER(doc, STR("count(/node/comment()/ancestor-or-self::node())"), 3);
 -}
 -
 -TEST_XML(xpath_xalan_position_7, "<chapter title='A'><section title='A1'><subsection title='A1a'>hello</subsection><subsection title='A1b'>ahoy</subsection></section><section title='A2'><subsection title='A2a'>goodbye</subsection><subsection title='A2b'>sayonara</subsection><subsection title='A2c'>adios</subsection></section><section title='A3'><subsection title='A3a'>aloha</subsection><subsection title='A3b'><footnote>A3b-1</footnote><footnote>A3b-2</footnote></subsection><subsection title='A3c'>shalom</subsection></section></chapter>")
 -{
 -	CHECK_XPATH_NODESET(doc, STR("chapter/section//@title[7]"));
 -	CHECK_XPATH_NODESET(doc, STR("(chapter/section//@title)[7]")) % 21;
 -}
 -
 -TEST_XML(xpath_xalan_match_1, "<root><x spot='a' num='1'/><x spot='b' num='2'/><x spot='c' num='3'/><x spot='d' num='4'/><x spot='e' num='5'/><x spot='f' num='6'/><x spot='g' num='7'/><x spot='h' num='8'/><x spot='i' num='9'/><x spot='j' num='10'/><x spot='k' num='11'/><x spot='l' num='12'/></root>")
 -{
 -	xml_node c = doc.child(STR("root"));
 -
 -	CHECK_XPATH_NODESET(c, STR("x[(position() mod 2)=1][position() > 3]")) % 21 % 27 % 33;
 -	CHECK_XPATH_NODESET(c, STR("x[(position() mod 2)=1][position() > 3][position()=2]")) % 27;
 -	CHECK_XPATH_NODESET(c, STR("x[(position() mod 2) > 0][position() > 3][2]")) % 27;
 -	CHECK_XPATH_NODESET(c, STR("x[(position() mod 2)=1][position() > 3][last()]")) % 33;
 -	CHECK_XPATH_NODESET(c, STR("x[(position() mod 2)=1][@num > 5][last()]")) % 33;
 -	CHECK_XPATH_NODESET(c, STR("x[(@num mod 3)=2][position() > 2][last()]")) % 33;
 -	CHECK_XPATH_NODESET(c, STR("x[(position() mod 2)=1][2][@num < 10]")) % 9;
 -	CHECK_XPATH_NODESET(c, STR("x[(((((2*10)-4)+9) div 5) mod 3)]")) % 6;
 -}
 -
 -
 -TEST_XML(xpath_xalan_match_2, "<doc><l1><v2>doc-l1-v2</v2><x2>doc-l1-x2</x2><l2><v3>doc-l1-l2-v3</v3><w3>doc-l1-l2-w3</w3><x3>doc-l1-l2-x3</x3><y3>doc-l1-l2-y3</y3><l3><v4>doc-l1-l2-l3-v4</v4><x4>doc-l1-l2-l3-x4</x4></l3></l2></l1></doc>")
 -{
 -	CHECK_XPATH_STRING(doc, STR("doc/l1/v2"), STR("doc-l1-v2"));
 -	CHECK_XPATH_STRING(doc, STR("doc/child::l1/x2"), STR("doc-l1-x2"));
 -	CHECK_XPATH_STRING(doc, STR("doc/l1//v3"), STR("doc-l1-l2-v3"));
 -	CHECK_XPATH_STRING(doc, STR("doc//l2/w3"), STR("doc-l1-l2-w3"));
 -	CHECK_XPATH_STRING(doc, STR("doc/child::l1//x3"), STR("doc-l1-l2-x3"));
 -	CHECK_XPATH_STRING(doc, STR("doc//child::l2/y3"), STR("doc-l1-l2-y3"));
 -	CHECK_XPATH_STRING(doc, STR("doc//l2//v4"), STR("doc-l1-l2-l3-v4"));
 -	CHECK_XPATH_STRING(doc, STR("doc//child::l2//x4"), STR("doc-l1-l2-l3-x4"));
 -
 -	CHECK_XPATH_STRING(doc, STR("doc/l1/v2"), STR("doc-l1-v2"));
 -	CHECK_XPATH_STRING(doc, STR("doc/l1/child::x2"), STR("doc-l1-x2"));
 -	CHECK_XPATH_STRING(doc, STR("doc/l1//v3"), STR("doc-l1-l2-v3"));
 -	CHECK_XPATH_STRING(doc, STR("doc//l2/w3"), STR("doc-l1-l2-w3"));
 -	CHECK_XPATH_STRING(doc, STR("doc/l1//child::x3"), STR("doc-l1-l2-x3"));
 -	CHECK_XPATH_STRING(doc, STR("doc//l2/child::y3"), STR("doc-l1-l2-y3"));
 -	CHECK_XPATH_STRING(doc, STR("doc//l2//v4"), STR("doc-l1-l2-l3-v4"));
 -	CHECK_XPATH_STRING(doc, STR("doc//l2//child::x4"), STR("doc-l1-l2-l3-x4"));
 -
 -	CHECK_XPATH_STRING(doc, STR("doc/l1/v2"), STR("doc-l1-v2"));
 -	CHECK_XPATH_STRING(doc, STR("doc/child::l1/child::x2"), STR("doc-l1-x2"));
 -	CHECK_XPATH_STRING(doc, STR("doc/l1//v3"), STR("doc-l1-l2-v3"));
 -	CHECK_XPATH_STRING(doc, STR("doc//l2/w3"), STR("doc-l1-l2-w3"));
 -	CHECK_XPATH_STRING(doc, STR("doc/child::l1//child::x3"), STR("doc-l1-l2-x3"));
 -	CHECK_XPATH_STRING(doc, STR("doc//child::l2/child::y3"), STR("doc-l1-l2-y3"));
 -	CHECK_XPATH_STRING(doc, STR("doc//l2//v4"), STR("doc-l1-l2-l3-v4"));
 -	CHECK_XPATH_STRING(doc, STR("doc//child::l2//child::x4"), STR("doc-l1-l2-l3-x4"));
 -}
 -
 -TEST_XML(xpath_xalan_match_3, "<doc><child><child-foo><name id='1'>John Doe</name><child><name id='2'>Jane Doe</name></child></child-foo></child></doc>")
 -{
 -	CHECK_XPATH_NODESET(doc, STR("doc/child/*[starts-with(name(),'child-')]//name")) % 5 % 9;
 -	CHECK_XPATH_NODESET(doc, STR("//@*")) % 6 % 10;
 -}
 -
 -TEST_XML(xpath_xalan_expression_1, "<doc><para id='1' xml:lang='en'>en</para><div xml:lang='en'><para>en</para></div><para id='3' xml:lang='EN'>EN</para><para id='4' xml:lang='en-us'>en-us</para></doc>")
 -{
 -	CHECK_XPATH_NODESET(doc, STR("doc/para[@id='1' and lang('en')]")) % 3;
 -	CHECK_XPATH_NODESET(doc, STR("doc/para[@id='4' and lang('en')]")) % 15;
 -	CHECK_XPATH_NODESET(doc, STR("doc/div/para[lang('en')]")) % 9;
 -	CHECK_XPATH_NODESET(doc, STR("doc/para[@id='3' and lang('en')]")) % 11;
 -	CHECK_XPATH_NODESET(doc, STR("//para[lang('en')]/ancestor-or-self::*[@xml:lang]/@xml:lang")) % 5 % 8 % 13 % 17;
 -}
 -
 -TEST_XML(xpath_xalan_predicate_1, "<doc><a>1</a><a>2</a><a>3</a><a>4</a></doc>")
 -{
 -	xml_node c = doc.child(STR("doc"));
 -
 -	CHECK_XPATH_NODESET(c, STR("a[true()=4]")) % 3 % 5 % 7 % 9;
 -	CHECK_XPATH_NODESET(c, STR("a[true()='stringwithchars']")) % 3 % 5 % 7 % 9;
 -	CHECK_XPATH_NODESET(c, STR("a[true()=following-sibling::*]")) % 3 % 5 % 7;
 -	CHECK_XPATH_NODESET(c, STR("a[true()=preceding-sibling::*]")) % 5 % 7 % 9;
 -	CHECK_XPATH_NODESET(c, STR("a[3=following-sibling::*]")) % 3 % 5;
 -	CHECK_XPATH_NODESET(c, STR("a[0 < true()]")) % 3 % 5 % 7 % 9;
 -	CHECK_XPATH_NODESET(c, STR("a['3.5' < 4]")) % 3 % 5 % 7 % 9;
 -	CHECK_XPATH_NODESET(c, STR("a[3 < following-sibling::*]")) % 3 % 5 % 7;
 -	CHECK_XPATH_NODESET(c, STR("a[following-sibling::*>3]")) % 3 % 5 % 7;
 -	CHECK_XPATH_NODESET(c, STR("a[3 > following-sibling::*]")) % 3;
 -	CHECK_XPATH_NODESET(c, STR("a[following-sibling::*<3]")) % 3;
 -	CHECK_XPATH_NODESET(c, STR("a[1 < 2 < 3]")) % 3 % 5 % 7 % 9;
 -	CHECK_XPATH_NODESET(c, STR("a[1 < 3 < 2]")) % 3 % 5 % 7 % 9;
 -	CHECK_XPATH_NODESET(c, STR("a[following-sibling::*=true()]")) % 3 % 5 % 7;
 -	CHECK_XPATH_NODESET(c, STR("a[false()!=following-sibling::*]")) % 3 % 5 % 7;
 -	CHECK_XPATH_NODESET(c, STR("a[following-sibling::*!=false()]")) % 3 % 5 % 7;
 -	CHECK_XPATH_NODESET(c, STR("a[following-sibling::*=3]")) % 3 % 5;
 -	CHECK_XPATH_NODESET(c, STR("a[3=following-sibling::*]")) % 3 % 5;
 -	CHECK_XPATH_NODESET(c, STR("a[4!=following-sibling::*]")) % 3 % 5;
 -	CHECK_XPATH_NODESET(c, STR("a[following-sibling::*!=4]")) % 3 % 5;
 -	CHECK_XPATH_NODESET(c, STR("a[3>=following-sibling::*]")) % 3 % 5;
 -	CHECK_XPATH_NODESET(c, STR("a[3<=following-sibling::*]")) % 3 % 5 % 7;
 -	CHECK_XPATH_NODESET(c, STR("a[following-sibling::*<=3]")) % 3 % 5;
 -	CHECK_XPATH_NODESET(c, STR("a[following-sibling::*>=3]")) % 3 % 5 % 7;
 -}
 -
 -TEST_XML(xpath_xalan_predicate_2, "<foo><bar a='0' b='0' c='0' d='0' seq='0'/><bar a='0' b='0' c='0' d='1' seq='1'/><bar a='0' b='0' c='1' d='0' seq='2'/><bar a='0' b='0' c='1' d='1' seq='3'/><bar a='0' b='1' c='0' d='0' seq='4'/><bar a='0' b='1' c='0' d='1' seq='5'/><bar a='0' b='1' c='1' d='0' seq='6'/><bar a='0' b='1' c='1' d='1' seq='7'/><bar a='1' b='0' c='0' d='0' seq='8'/><bar a='1' b='0' c='0' d='1' seq='9'/><bar a='1' b='0' c='1' d='0' seq='a'/><bar a='1' b='0' c='1' d='1' seq='b'/><bar a='1' b='1' c='0' d='0' seq='c'/><bar a='1' b='1' c='0' d='1' seq='d'/><bar a='1' b='1' c='1' d='0' seq='e'/><bar a='1' b='1' c='1' d='1' seq='f'/></foo>")
 -{
 -	xml_node c = doc.child(STR("foo"));
 -
 -	CHECK_XPATH_NODESET(c, STR("bar[@a='1' and @b='1']")) % 75 % 81 % 87 % 93;
 -	CHECK_XPATH_NODESET(c, STR("bar[(@a='1' or @b='1') and @c='1']")) % 39 % 45 % 63 % 69 % 87 % 93;
 -	CHECK_XPATH_NODESET(c, STR("bar[@a='1' and (@b='1' or @c='1') and @d='1']")) % 69 % 81 % 93;
 -	CHECK_XPATH_NODESET(c, STR("bar[@a='1' and @b='1' or @c='1' and @d='1']")) % 21 % 45 % 69 % 75 % 81 % 87 % 93;
 -	CHECK_XPATH_NODESET(c, STR("bar[(@a='1' and @b='1') or (@c='1' and @d='1')]")) % 21 % 45 % 69 % 75 % 81 % 87 % 93;
 -	CHECK_XPATH_NODESET(c, STR("bar[@a='1' or (@b='1' and @c='1') or @d='1']")) % 9 % 21 % 33 % 39 % 45 % 51 % 57 % 63 % 69 % 75 % 81 % 87 % 93;
 -	CHECK_XPATH_NODESET(c, STR("bar[(@a='1' or @b='1') and (@c='1' or @d='1')]")) % 33 % 39 % 45 % 57 % 63 % 69 % 81 % 87 % 93;
 -	CHECK_XPATH_NODESET(c, STR("bar[@a='1' or @b='1' and @c='1' or @d='1']")) % 9 % 21 % 33 % 39 % 45 % 51 % 57 % 63 % 69 % 75 % 81 % 87 % 93;
 -	CHECK_XPATH_NODESET(c, STR("bar[@a='1' or @b='1' or @c='1']")) % 15 % 21 % 27 % 33 % 39 % 45 % 51 % 57 % 63 % 69 % 75 % 81 % 87 % 93;
 -}
 -
 -TEST_XML(xpath_xalan_predicate_3, "<doc><a>1</a><a ex=''>2</a><a ex='value'>3</a><a why=''>4</a><a why='value'>5</a></doc>")
 -{
 -	xml_node c = doc.child(STR("doc"));
 -
 -	CHECK_XPATH_NUMBER(c, STR("count(a[@ex])"), 2);
 -	CHECK_XPATH_NUMBER(c, STR("count(a[@ex=''])"), 1);
 -	CHECK_XPATH_NUMBER(c, STR("count(a[string-length(@ex)=0])"), 4);
 -	CHECK_XPATH_NUMBER(c, STR("count(a[@ex!=''])"), 1);
 -	CHECK_XPATH_NUMBER(c, STR("count(a[string-length(@ex) > 0])"), 1);
 -	CHECK_XPATH_NUMBER(c, STR("count(a[not(@ex)])"), 3);
 -	CHECK_XPATH_NUMBER(c, STR("count(a[not(@ex='')])"), 4);
 -	CHECK_XPATH_NUMBER(c, STR("count(a[not(string-length(@ex)=0)])"), 1);
 -	CHECK_XPATH_NUMBER(c, STR("count(a[@why='value'])"), 1);
 -	CHECK_XPATH_NUMBER(c, STR("count(a[@why!='value'])"), 1);
 -}
 -
 -TEST_XML(xpath_xalan_predicate_4, "<table><tr><td>1.1</td><td>1.2</td></tr><tr><td>2.1</td><td>2.2</td><td>2.3</td></tr><tr><td>3.1</td><td>3.2<td>3.2.1</td></td></tr><tr><td>4<td>4.1<td>4.1.1</td></td></td></tr><tr><td>5.1</td><td>5.2</td><td>5.3</td><td>5.4</td></tr><tr><ta/><td>6.1</td><td>6.2</td></tr><tr><ta/><td>7.1</td><td>7.2</td><td>7.3</td></tr><tr><ta/><td>8.1</td><td>8.2</td><td>8.3</td><td>8.4</td></tr></table>")
 -{
 -	CHECK_XPATH_NUMBER(doc, STR("count(//tr)"), 8);
 -	CHECK_XPATH_NUMBER(doc, STR("count(//tr[count(./td)=3])"), 2);
 -}
 -
 -TEST_XML(xpath_xalan_predicate_5, "<doc><element1>Wrong node selected!!</element1><element1>Test executed successfully</element1><element1>Wrong node selected!!</element1></doc>")
 -{
 -	CHECK_XPATH_STRING(doc, STR("doc/element1[(((((2*10)-4)+9) div 5) mod 3 )]"), STR("Test executed successfully"));
 -	CHECK_XPATH_STRING(doc, STR("doc/element1[(((((2*10)-4)+9) div 5) mod floor(3))]"), STR("Test executed successfully"));
 -	CHECK_XPATH_STRING(doc, STR("doc/element1[floor(2)]"), STR("Test executed successfully"));
 -}
 -
 -TEST_XML(xpath_xalan_predicate_6, "<doc><a>1</a><a>2<achild>target</achild></a><a>3</a><a>4</a></doc>")
 -{
 -	CHECK_XPATH_STRING(doc, STR("doc/a['target'=descendant::*]"), STR("2target"));
 -	CHECK_XPATH_STRING(doc, STR("doc/a[descendant::*='target']"), STR("2target"));
 -}
 -
 -TEST_XML(xpath_xalan_predicate_7, "<doc><a>1</a><a>2<achild>target</achild></a><a>3</a><a>4<achild>missed</achild></a></doc>")
 -{
 -	CHECK_XPATH_STRING(doc, STR("doc/a['target'!=descendant::*]"), STR("4missed"));
 -	CHECK_XPATH_STRING(doc, STR("doc/a[descendant::*!='target']"), STR("4missed"));
 -}
 -
 -TEST_XML(xpath_xalan_predicate_8, "<doc><foo><bar attr='1'>this</bar><bar attr='2'>2</bar><bar attr='3'>3</bar></foo><foo><bar attr='4'>this</bar><bar attr='5'>this</bar><bar1 attr='6'>that</bar1></foo><foo><bar attr='7'><baz attr='a'>hello</baz><baz attr='b'>goodbye</baz></bar><bar2 attr='8'>this</bar2><bar2 attr='9'>that</bar2></foo><foo><bar attr='10'>this</bar><bar attr='11'><baz attr='a'>hello</baz><baz attr='b'>goodbye</baz></bar><bar attr='12'>other</bar></foo></doc>")
 -{
 -	xml_node c = doc.child(STR("doc"));
 -
 -	CHECK_XPATH_NODESET(c, STR("foo[(bar[2])='this']")) % 13;
 -	CHECK_XPATH_NODESET(c, STR("foo[(bar[(baz[2])='goodbye'])]")) % 23 % 38;
 -	CHECK_XPATH_NODESET(c, STR("foo[(bar[2][(baz[2])='goodbye'])]")) % 38;
 -}
 -
 -TEST_XML(xpath_xalan_predicate_9, "<doc><a><asub><asubsub/></asub></a><b><bsub><foo><child/></foo></bsub></b><c>f-inside</c><d><dsub><dsubsub><foundnode/></dsubsub></dsub></d><e>f-inside<esub>f-inside</esub><esubsib>f-inside</esubsib>f-inside</e><f><fsub/></f></doc>")
 -{
 -	CHECK_XPATH_NODESET(doc, STR("doc/*[starts-with(name(.),'f')]")) % 23;
 -	CHECK_XPATH_NODESET(doc, STR("//*[starts-with(name(.),'f')]")) % 8 % 15 % 23 % 24;
 -}
 -
 -TEST_XML(xpath_xalan_predicate_10, "<doc><element1>Text from first element<child1>Text from child1 of first element</child1><child2>Text from child2 of first element</child2></element1><element2>Text from second element<child1>Text from child1 of second element</child1><child2 attr1='yes'>Text from child2 of second element (correct execution)</child2></element2></doc>")
 -{
 -	xml_node c = doc.child(STR("doc"));
 -
 -	CHECK_XPATH_STRING(c, STR("//child2[ancestor::element2]"), STR("Text from child2 of second element (correct execution)"));
 -	CHECK_XPATH_STRING(c, STR("//child2[ancestor-or-self::element2]"), STR("Text from child2 of second element (correct execution)"));
 -	CHECK_XPATH_STRING(c, STR("//child2[attribute::attr1]"), STR("Text from child2 of second element (correct execution)"));
 -}
 -
 -TEST_XML(xpath_xalan_predicate_11, "<doc><a squish='heavy' squash='butternut'>1</a><a squish='heavy' squeesh='virus'>2</a><a squash='butternut' squeesh='virus'>3</a><a squish='heavy'>4</a><a squeesh='virus'>5</a><a squash='butternut'>6</a></doc>")
 -{
 -	xml_node c = doc.child(STR("doc"));
 -
 -	CHECK_XPATH_NODESET(c, STR("a[@squeesh or (@squish and @squash)]")) % 3 % 7 % 11 % 18;
 -	CHECK_XPATH_NODESET(c, STR("a[(@squeesh or @squish) and @squash]")) % 3 % 11;
 -	CHECK_XPATH_NODESET(c, STR("a[@squeesh or @squish and @squash]")) % 3 % 7 % 11 % 18;
 -}
 -
 -TEST_XML(xpath_xalan_predicate_12, "<doc><a>1</a><a>2<achild>target</achild></a><a>3</a><a>target</a></doc>")
 -{
 -	CHECK_XPATH_STRING(doc, STR("doc/a[following-sibling::*=descendant::*]"), STR("2target"));
 -}
 -
 -TEST_XML(xpath_xalan_predicate_13, "<doc><a squish='heavy'>1</a><a>2<achild>target</achild></a><a>3</a></doc>")
 -{
 -	CHECK_XPATH_NODESET(doc, STR("doc/a[('target'=descendant::*) or @squish]")) % 3 % 6;
 -	CHECK_XPATH_NODESET(doc, STR("doc/a[not(('target'=descendant::*) or @squish)]")) % 10;
 -}
 -
 -TEST_XML(xpath_xalan_predicate_14, "<doc><a squish='heavy'>1</a><a>2<achild size='large'>child2</achild></a><a>3</a><a attrib='present'>4<achild>child4</achild></a></doc>")
 -{
 -	CHECK_XPATH_NODESET(doc, STR("doc/a[not(@*)]")) % 6 % 11;
 -}
 -
 -TEST_XML(xpath_xalan_predicate_15, "<doc><a><asub><asubsub/></asub></a><b><bsub>x</bsub></b><c>inside</c><d><dsub><q><foundnode/></q></dsub></d></doc>")
 -{
 -	CHECK_XPATH_NODESET(doc, STR("doc/descendant::*[string-length(name(.))=1]")) % 3 % 6 % 9 % 11 % 13;
 -}
 -
 -#endif
 +#ifndef PUGIXML_NO_XPATH + +#include "common.hpp" + +TEST_XML(xpath_xalan_position_1, "<doc><a>1</a><a>2</a><a>3</a><a>4</a></doc>") +{ +	xml_node c = doc.child(STR("doc")); + +	CHECK_XPATH_BOOLEAN(c, STR("position()=1"), true); +	CHECK_XPATH_NODESET(c, STR("*[position()=4]")) % 9; +} + +TEST_XML_FLAGS(xpath_xalan_position_2, "<doc><a test='true'><num>1</num></a><a><num>1191</num></a><a><num>263</num></a><a test='true'><num>2</num></a><a><num>827</num></a><a><num>256</num></a><a test='true'><num>3</num></a><a test='true'><num>4<x/>5</num></a><?pi?><?pi?><!--comment--><!--comment--></doc>", parse_default | parse_comments | parse_pi) +{ +	xml_node c = doc.child(STR("doc")); + +	CHECK_XPATH_NODESET(c, STR("*[@test and position()=8]")) % 27; +	CHECK_XPATH_NODESET(c, STR("*[@test][position()=4]/num")) % 29; +	CHECK_XPATH_NUMBER(c, STR("count(*)"), 8); +	CHECK_XPATH_NODESET(c, STR("*[last()=position()]")) % 27; +	CHECK_XPATH_NODESET(c, STR("a[position()=2]")) % 7; +	CHECK_XPATH_NODESET(c, STR("a[3]/../a[position()=4]/num/../@test")) % 14; +	CHECK_XPATH_BOOLEAN(c, STR("not(position()=last())"), false); +	CHECK_XPATH_BOOLEAN(c, STR("position()=2"), false); +	CHECK_XPATH_BOOLEAN(c, STR("last()=1"), true); +	CHECK_XPATH_BOOLEAN(c, STR("last()+2=3"), true); +	CHECK_XPATH_NODESET(c, STR("a[position()=5 mod 3]")) % 7; +	CHECK_XPATH_NODESET(c, STR("a/num/text()[position()=1]")) % 6 % 9 % 12 % 16 % 19 % 22 % 26 % 30; +	CHECK_XPATH_NODESET(c, STR("a/num/text()[position()=2]")) % 32; +	CHECK_XPATH_NODESET(c, STR("a/num/text()[position()=last()]")) % 6 % 9 % 12 % 16 % 19 % 22 % 26 % 32; +	CHECK_XPATH_NODESET(c, STR("a/num/text()[1]")) % 6 % 9 % 12 % 16 % 19 % 22 % 26 % 30; +	CHECK_XPATH_NODESET(c, STR("a/num/text()[2]")) % 32; +	CHECK_XPATH_NODESET(c, STR("a/num/text()[last()]")) % 6 % 9 % 12 % 16 % 19 % 22 % 26 % 32; +	CHECK_XPATH_NODESET(c, STR("a[floor(last() div 3)]")) % 7; +	CHECK_XPATH_NODESET(c, STR("a[ceiling(last() div 3)]")) % 10; +	CHECK_XPATH_NODESET(c, STR("a[round(last() div 3)]")) % 10; +	CHECK_XPATH_NODESET(c, STR("a[last() div 3]")); +	CHECK_XPATH_NODESET(c, STR("a[last() div 2]")) % 13; +	CHECK_XPATH_NODESET(c, STR("a[3]/../a[position()>=2 and position()<=4]")) % 7 % 10 % 13; +	CHECK_XPATH_NUMBER(c, STR("count(a[position()>=2 and position()<=4]/num)"), 3); +	CHECK_XPATH_NUMBER(c, STR("count(a/@*)"), 4); +	CHECK_XPATH_NUMBER(c, STR("count(a/attribute::*)"), 4); +	CHECK_XPATH_NODESET(c, STR("*[not(@test)][position()=last()]")) % 20; +	CHECK_XPATH_NODESET(c, STR("*[not(@test)][last()]")) % 20; +	CHECK_XPATH_NODESET(c, STR("a[3-2]")) % 3; +	CHECK_XPATH_NODESET(c, STR("a[0]")); +	CHECK_XPATH_NODESET(c, STR("a[9]")); +	CHECK_XPATH_NODESET(c, STR("a['3']")) % 3 % 7 % 10 % 13 % 17 % 20 % 23 % 27; +	CHECK_XPATH_NODESET(c, STR("a[number('3')]")) % 10; +	CHECK_XPATH_NODESET(c, STR("processing-instruction()[2]")) % 34; +	CHECK_XPATH_NODESET(c, STR("processing-instruction('pi')[2]")) % 34; +	CHECK_XPATH_NODESET(c, STR("comment()[2]")) % 36; +    CHECK_XPATH_NODESET(c, STR("a/*[last()]")) % 5 % 8 % 11 % 15 % 18 % 21 % 25 % 29; +    CHECK_XPATH_NODESET(c, STR("a/child::*[last()]")) % 5 % 8 % 11 % 15 % 18 % 21 % 25 % 29; +    CHECK_XPATH_NODESET(c, STR("a/descendant::*[last()]")) % 5 % 8 % 11 % 15 % 18 % 21 % 25 % 31; +    CHECK_XPATH_NODESET(c, STR("a/child::node()[last()]")) % 5 % 8 % 11 % 15 % 18 % 21 % 25 % 29; +    CHECK_XPATH_NODESET(c, STR("a/descendant::text()[last()]")) % 6 % 9 % 12 % 16 % 19 % 22 % 26 % 32; +    CHECK_XPATH_NODESET(c, STR("child::comment()[last()]")) % 36; +} + +TEST_XML(xpath_xalan_position_3, "<article class='whitepaper' status='Note'><articleinfo><title>AAA</title><section id='info'><title>BBB</title><para>About this article</para><section revisionflag='added'><title>CCC</title><para>This is the section titled 'ZZZ'.</para><ednote who='KKK'><title>DDD</title><para>Don't worry.</para><section revisionflag='added'><title>EEE</title><para>This is the deep subsection.</para></section></ednote></section></section></articleinfo></article>") +{ +	CHECK_XPATH_NODESET(doc, STR("(article//section/title|/articleinfo/title|article/section/para)[last()]")) % 28; +	CHECK_XPATH_NODESET(doc, STR("(article//section/title|/articleinfo/title|article/section/para)[1]")) % 10; +	CHECK_XPATH_NUMBER(doc, STR("count(article/articleinfo/section[last()])"), 1); +	CHECK_XPATH_NUMBER(doc, STR("count(article/articleinfo/section[last()][title='BBB'])"), 1); +} + +TEST_XML(xpath_xalan_position_4, "<chapter><section><footnote>hello</footnote></section><section><footnote>goodbye</footnote><footnote>sayonara</footnote></section><section><footnote>aloha</footnote></section></chapter>") +{ +	CHECK_XPATH_NODESET(doc, STR("chapter//footnote[1]")) % 4 % 7 % 12; +} + +TEST_XML(xpath_xalan_position_5, "<chapter><section><footnote>hello</footnote><footnote>ahoy</footnote></section><section><footnote>goodbye</footnote><footnote>sayonara</footnote><footnote>adios</footnote></section><section><footnote>aloha</footnote><subsection><footnote>shalom</footnote><footnote>yo</footnote></subsection><footnote>ciao</footnote></section></chapter>") +{ +	CHECK_XPATH_NODESET(doc, STR("chapter//footnote[2]")) % 6 % 11 % 21 % 23; +	CHECK_XPATH_NODESET(doc, STR("(chapter//footnote)[2]")) % 6; +	CHECK_XPATH_NODESET(doc, STR("(child::chapter/descendant-or-self::node())/footnote[2]")) % 6 % 11 % 21 % 23; +	CHECK_XPATH_NODESET(doc, STR("chapter/descendant::footnote[6]")) % 16; +	CHECK_XPATH_NODESET(doc, STR("chapter/descendant::footnote[6][1][last()]")) % 16; +} + +TEST_XML_FLAGS(xpath_xalan_position_6, "<node attr='value'>pcdata<child/><?pi1 value?><?pi2 value?><!--comment--><![CDATA[cdata]]></node>", parse_default | parse_pi | parse_comments) +{ +	CHECK_XPATH_NUMBER(doc, STR("count(/node/@attr/ancestor-or-self::node())"), 3); +	CHECK_XPATH_NUMBER(doc, STR("count(/node/text()/ancestor-or-self::node())"), 4); +	CHECK_XPATH_NUMBER(doc, STR("count(/node/processing-instruction()/ancestor-or-self::node())"), 4); +	CHECK_XPATH_NUMBER(doc, STR("count(/node/processing-instruction('pi1')/ancestor-or-self::node())"), 3); +	CHECK_XPATH_NUMBER(doc, STR("count(/node/comment()/ancestor-or-self::node())"), 3); +} + +TEST_XML(xpath_xalan_position_7, "<chapter title='A'><section title='A1'><subsection title='A1a'>hello</subsection><subsection title='A1b'>ahoy</subsection></section><section title='A2'><subsection title='A2a'>goodbye</subsection><subsection title='A2b'>sayonara</subsection><subsection title='A2c'>adios</subsection></section><section title='A3'><subsection title='A3a'>aloha</subsection><subsection title='A3b'><footnote>A3b-1</footnote><footnote>A3b-2</footnote></subsection><subsection title='A3c'>shalom</subsection></section></chapter>") +{ +	CHECK_XPATH_NODESET(doc, STR("chapter/section//@title[7]")); +	CHECK_XPATH_NODESET(doc, STR("(chapter/section//@title)[7]")) % 21; +} + +TEST_XML(xpath_xalan_match_1, "<root><x spot='a' num='1'/><x spot='b' num='2'/><x spot='c' num='3'/><x spot='d' num='4'/><x spot='e' num='5'/><x spot='f' num='6'/><x spot='g' num='7'/><x spot='h' num='8'/><x spot='i' num='9'/><x spot='j' num='10'/><x spot='k' num='11'/><x spot='l' num='12'/></root>") +{ +	xml_node c = doc.child(STR("root")); + +	CHECK_XPATH_NODESET(c, STR("x[(position() mod 2)=1][position() > 3]")) % 21 % 27 % 33; +	CHECK_XPATH_NODESET(c, STR("x[(position() mod 2)=1][position() > 3][position()=2]")) % 27; +	CHECK_XPATH_NODESET(c, STR("x[(position() mod 2) > 0][position() > 3][2]")) % 27; +	CHECK_XPATH_NODESET(c, STR("x[(position() mod 2)=1][position() > 3][last()]")) % 33; +	CHECK_XPATH_NODESET(c, STR("x[(position() mod 2)=1][@num > 5][last()]")) % 33; +	CHECK_XPATH_NODESET(c, STR("x[(@num mod 3)=2][position() > 2][last()]")) % 33; +	CHECK_XPATH_NODESET(c, STR("x[(position() mod 2)=1][2][@num < 10]")) % 9; +	CHECK_XPATH_NODESET(c, STR("x[(((((2*10)-4)+9) div 5) mod 3)]")) % 6; +} + + +TEST_XML(xpath_xalan_match_2, "<doc><l1><v2>doc-l1-v2</v2><x2>doc-l1-x2</x2><l2><v3>doc-l1-l2-v3</v3><w3>doc-l1-l2-w3</w3><x3>doc-l1-l2-x3</x3><y3>doc-l1-l2-y3</y3><l3><v4>doc-l1-l2-l3-v4</v4><x4>doc-l1-l2-l3-x4</x4></l3></l2></l1></doc>") +{ +	CHECK_XPATH_STRING(doc, STR("doc/l1/v2"), STR("doc-l1-v2")); +	CHECK_XPATH_STRING(doc, STR("doc/child::l1/x2"), STR("doc-l1-x2")); +	CHECK_XPATH_STRING(doc, STR("doc/l1//v3"), STR("doc-l1-l2-v3")); +	CHECK_XPATH_STRING(doc, STR("doc//l2/w3"), STR("doc-l1-l2-w3")); +	CHECK_XPATH_STRING(doc, STR("doc/child::l1//x3"), STR("doc-l1-l2-x3")); +	CHECK_XPATH_STRING(doc, STR("doc//child::l2/y3"), STR("doc-l1-l2-y3")); +	CHECK_XPATH_STRING(doc, STR("doc//l2//v4"), STR("doc-l1-l2-l3-v4")); +	CHECK_XPATH_STRING(doc, STR("doc//child::l2//x4"), STR("doc-l1-l2-l3-x4")); + +	CHECK_XPATH_STRING(doc, STR("doc/l1/v2"), STR("doc-l1-v2")); +	CHECK_XPATH_STRING(doc, STR("doc/l1/child::x2"), STR("doc-l1-x2")); +	CHECK_XPATH_STRING(doc, STR("doc/l1//v3"), STR("doc-l1-l2-v3")); +	CHECK_XPATH_STRING(doc, STR("doc//l2/w3"), STR("doc-l1-l2-w3")); +	CHECK_XPATH_STRING(doc, STR("doc/l1//child::x3"), STR("doc-l1-l2-x3")); +	CHECK_XPATH_STRING(doc, STR("doc//l2/child::y3"), STR("doc-l1-l2-y3")); +	CHECK_XPATH_STRING(doc, STR("doc//l2//v4"), STR("doc-l1-l2-l3-v4")); +	CHECK_XPATH_STRING(doc, STR("doc//l2//child::x4"), STR("doc-l1-l2-l3-x4")); + +	CHECK_XPATH_STRING(doc, STR("doc/l1/v2"), STR("doc-l1-v2")); +	CHECK_XPATH_STRING(doc, STR("doc/child::l1/child::x2"), STR("doc-l1-x2")); +	CHECK_XPATH_STRING(doc, STR("doc/l1//v3"), STR("doc-l1-l2-v3")); +	CHECK_XPATH_STRING(doc, STR("doc//l2/w3"), STR("doc-l1-l2-w3")); +	CHECK_XPATH_STRING(doc, STR("doc/child::l1//child::x3"), STR("doc-l1-l2-x3")); +	CHECK_XPATH_STRING(doc, STR("doc//child::l2/child::y3"), STR("doc-l1-l2-y3")); +	CHECK_XPATH_STRING(doc, STR("doc//l2//v4"), STR("doc-l1-l2-l3-v4")); +	CHECK_XPATH_STRING(doc, STR("doc//child::l2//child::x4"), STR("doc-l1-l2-l3-x4")); +} + +TEST_XML(xpath_xalan_match_3, "<doc><child><child-foo><name id='1'>John Doe</name><child><name id='2'>Jane Doe</name></child></child-foo></child></doc>") +{ +	CHECK_XPATH_NODESET(doc, STR("doc/child/*[starts-with(name(),'child-')]//name")) % 5 % 9; +	CHECK_XPATH_NODESET(doc, STR("//@*")) % 6 % 10; +} + +TEST_XML(xpath_xalan_expression_1, "<doc><para id='1' xml:lang='en'>en</para><div xml:lang='en'><para>en</para></div><para id='3' xml:lang='EN'>EN</para><para id='4' xml:lang='en-us'>en-us</para></doc>") +{ +	CHECK_XPATH_NODESET(doc, STR("doc/para[@id='1' and lang('en')]")) % 3; +	CHECK_XPATH_NODESET(doc, STR("doc/para[@id='4' and lang('en')]")) % 15; +	CHECK_XPATH_NODESET(doc, STR("doc/div/para[lang('en')]")) % 9; +	CHECK_XPATH_NODESET(doc, STR("doc/para[@id='3' and lang('en')]")) % 11; +	CHECK_XPATH_NODESET(doc, STR("//para[lang('en')]/ancestor-or-self::*[@xml:lang]/@xml:lang")) % 5 % 8 % 13 % 17; +} + +TEST_XML(xpath_xalan_predicate_1, "<doc><a>1</a><a>2</a><a>3</a><a>4</a></doc>") +{ +	xml_node c = doc.child(STR("doc")); + +	CHECK_XPATH_NODESET(c, STR("a[true()=4]")) % 3 % 5 % 7 % 9; +	CHECK_XPATH_NODESET(c, STR("a[true()='stringwithchars']")) % 3 % 5 % 7 % 9; +	CHECK_XPATH_NODESET(c, STR("a[true()=following-sibling::*]")) % 3 % 5 % 7; +	CHECK_XPATH_NODESET(c, STR("a[true()=preceding-sibling::*]")) % 5 % 7 % 9; +	CHECK_XPATH_NODESET(c, STR("a[3=following-sibling::*]")) % 3 % 5; +	CHECK_XPATH_NODESET(c, STR("a[0 < true()]")) % 3 % 5 % 7 % 9; +	CHECK_XPATH_NODESET(c, STR("a['3.5' < 4]")) % 3 % 5 % 7 % 9; +	CHECK_XPATH_NODESET(c, STR("a[3 < following-sibling::*]")) % 3 % 5 % 7; +	CHECK_XPATH_NODESET(c, STR("a[following-sibling::*>3]")) % 3 % 5 % 7; +	CHECK_XPATH_NODESET(c, STR("a[3 > following-sibling::*]")) % 3; +	CHECK_XPATH_NODESET(c, STR("a[following-sibling::*<3]")) % 3; +	CHECK_XPATH_NODESET(c, STR("a[1 < 2 < 3]")) % 3 % 5 % 7 % 9; +	CHECK_XPATH_NODESET(c, STR("a[1 < 3 < 2]")) % 3 % 5 % 7 % 9; +	CHECK_XPATH_NODESET(c, STR("a[following-sibling::*=true()]")) % 3 % 5 % 7; +	CHECK_XPATH_NODESET(c, STR("a[false()!=following-sibling::*]")) % 3 % 5 % 7; +	CHECK_XPATH_NODESET(c, STR("a[following-sibling::*!=false()]")) % 3 % 5 % 7; +	CHECK_XPATH_NODESET(c, STR("a[following-sibling::*=3]")) % 3 % 5; +	CHECK_XPATH_NODESET(c, STR("a[3=following-sibling::*]")) % 3 % 5; +	CHECK_XPATH_NODESET(c, STR("a[4!=following-sibling::*]")) % 3 % 5; +	CHECK_XPATH_NODESET(c, STR("a[following-sibling::*!=4]")) % 3 % 5; +	CHECK_XPATH_NODESET(c, STR("a[3>=following-sibling::*]")) % 3 % 5; +	CHECK_XPATH_NODESET(c, STR("a[3<=following-sibling::*]")) % 3 % 5 % 7; +	CHECK_XPATH_NODESET(c, STR("a[following-sibling::*<=3]")) % 3 % 5; +	CHECK_XPATH_NODESET(c, STR("a[following-sibling::*>=3]")) % 3 % 5 % 7; +} + +TEST_XML(xpath_xalan_predicate_2, "<foo><bar a='0' b='0' c='0' d='0' seq='0'/><bar a='0' b='0' c='0' d='1' seq='1'/><bar a='0' b='0' c='1' d='0' seq='2'/><bar a='0' b='0' c='1' d='1' seq='3'/><bar a='0' b='1' c='0' d='0' seq='4'/><bar a='0' b='1' c='0' d='1' seq='5'/><bar a='0' b='1' c='1' d='0' seq='6'/><bar a='0' b='1' c='1' d='1' seq='7'/><bar a='1' b='0' c='0' d='0' seq='8'/><bar a='1' b='0' c='0' d='1' seq='9'/><bar a='1' b='0' c='1' d='0' seq='a'/><bar a='1' b='0' c='1' d='1' seq='b'/><bar a='1' b='1' c='0' d='0' seq='c'/><bar a='1' b='1' c='0' d='1' seq='d'/><bar a='1' b='1' c='1' d='0' seq='e'/><bar a='1' b='1' c='1' d='1' seq='f'/></foo>") +{ +	xml_node c = doc.child(STR("foo")); + +	CHECK_XPATH_NODESET(c, STR("bar[@a='1' and @b='1']")) % 75 % 81 % 87 % 93; +	CHECK_XPATH_NODESET(c, STR("bar[(@a='1' or @b='1') and @c='1']")) % 39 % 45 % 63 % 69 % 87 % 93; +	CHECK_XPATH_NODESET(c, STR("bar[@a='1' and (@b='1' or @c='1') and @d='1']")) % 69 % 81 % 93; +	CHECK_XPATH_NODESET(c, STR("bar[@a='1' and @b='1' or @c='1' and @d='1']")) % 21 % 45 % 69 % 75 % 81 % 87 % 93; +	CHECK_XPATH_NODESET(c, STR("bar[(@a='1' and @b='1') or (@c='1' and @d='1')]")) % 21 % 45 % 69 % 75 % 81 % 87 % 93; +	CHECK_XPATH_NODESET(c, STR("bar[@a='1' or (@b='1' and @c='1') or @d='1']")) % 9 % 21 % 33 % 39 % 45 % 51 % 57 % 63 % 69 % 75 % 81 % 87 % 93; +	CHECK_XPATH_NODESET(c, STR("bar[(@a='1' or @b='1') and (@c='1' or @d='1')]")) % 33 % 39 % 45 % 57 % 63 % 69 % 81 % 87 % 93; +	CHECK_XPATH_NODESET(c, STR("bar[@a='1' or @b='1' and @c='1' or @d='1']")) % 9 % 21 % 33 % 39 % 45 % 51 % 57 % 63 % 69 % 75 % 81 % 87 % 93; +	CHECK_XPATH_NODESET(c, STR("bar[@a='1' or @b='1' or @c='1']")) % 15 % 21 % 27 % 33 % 39 % 45 % 51 % 57 % 63 % 69 % 75 % 81 % 87 % 93; +} + +TEST_XML(xpath_xalan_predicate_3, "<doc><a>1</a><a ex=''>2</a><a ex='value'>3</a><a why=''>4</a><a why='value'>5</a></doc>") +{ +	xml_node c = doc.child(STR("doc")); + +	CHECK_XPATH_NUMBER(c, STR("count(a[@ex])"), 2); +	CHECK_XPATH_NUMBER(c, STR("count(a[@ex=''])"), 1); +	CHECK_XPATH_NUMBER(c, STR("count(a[string-length(@ex)=0])"), 4); +	CHECK_XPATH_NUMBER(c, STR("count(a[@ex!=''])"), 1); +	CHECK_XPATH_NUMBER(c, STR("count(a[string-length(@ex) > 0])"), 1); +	CHECK_XPATH_NUMBER(c, STR("count(a[not(@ex)])"), 3); +	CHECK_XPATH_NUMBER(c, STR("count(a[not(@ex='')])"), 4); +	CHECK_XPATH_NUMBER(c, STR("count(a[not(string-length(@ex)=0)])"), 1); +	CHECK_XPATH_NUMBER(c, STR("count(a[@why='value'])"), 1); +	CHECK_XPATH_NUMBER(c, STR("count(a[@why!='value'])"), 1); +} + +TEST_XML(xpath_xalan_predicate_4, "<table><tr><td>1.1</td><td>1.2</td></tr><tr><td>2.1</td><td>2.2</td><td>2.3</td></tr><tr><td>3.1</td><td>3.2<td>3.2.1</td></td></tr><tr><td>4<td>4.1<td>4.1.1</td></td></td></tr><tr><td>5.1</td><td>5.2</td><td>5.3</td><td>5.4</td></tr><tr><ta/><td>6.1</td><td>6.2</td></tr><tr><ta/><td>7.1</td><td>7.2</td><td>7.3</td></tr><tr><ta/><td>8.1</td><td>8.2</td><td>8.3</td><td>8.4</td></tr></table>") +{ +	CHECK_XPATH_NUMBER(doc, STR("count(//tr)"), 8); +	CHECK_XPATH_NUMBER(doc, STR("count(//tr[count(./td)=3])"), 2); +} + +TEST_XML(xpath_xalan_predicate_5, "<doc><element1>Wrong node selected!!</element1><element1>Test executed successfully</element1><element1>Wrong node selected!!</element1></doc>") +{ +	CHECK_XPATH_STRING(doc, STR("doc/element1[(((((2*10)-4)+9) div 5) mod 3 )]"), STR("Test executed successfully")); +	CHECK_XPATH_STRING(doc, STR("doc/element1[(((((2*10)-4)+9) div 5) mod floor(3))]"), STR("Test executed successfully")); +	CHECK_XPATH_STRING(doc, STR("doc/element1[floor(2)]"), STR("Test executed successfully")); +} + +TEST_XML(xpath_xalan_predicate_6, "<doc><a>1</a><a>2<achild>target</achild></a><a>3</a><a>4</a></doc>") +{ +	CHECK_XPATH_STRING(doc, STR("doc/a['target'=descendant::*]"), STR("2target")); +	CHECK_XPATH_STRING(doc, STR("doc/a[descendant::*='target']"), STR("2target")); +} + +TEST_XML(xpath_xalan_predicate_7, "<doc><a>1</a><a>2<achild>target</achild></a><a>3</a><a>4<achild>missed</achild></a></doc>") +{ +	CHECK_XPATH_STRING(doc, STR("doc/a['target'!=descendant::*]"), STR("4missed")); +	CHECK_XPATH_STRING(doc, STR("doc/a[descendant::*!='target']"), STR("4missed")); +} + +TEST_XML(xpath_xalan_predicate_8, "<doc><foo><bar attr='1'>this</bar><bar attr='2'>2</bar><bar attr='3'>3</bar></foo><foo><bar attr='4'>this</bar><bar attr='5'>this</bar><bar1 attr='6'>that</bar1></foo><foo><bar attr='7'><baz attr='a'>hello</baz><baz attr='b'>goodbye</baz></bar><bar2 attr='8'>this</bar2><bar2 attr='9'>that</bar2></foo><foo><bar attr='10'>this</bar><bar attr='11'><baz attr='a'>hello</baz><baz attr='b'>goodbye</baz></bar><bar attr='12'>other</bar></foo></doc>") +{ +	xml_node c = doc.child(STR("doc")); + +	CHECK_XPATH_NODESET(c, STR("foo[(bar[2])='this']")) % 13; +	CHECK_XPATH_NODESET(c, STR("foo[(bar[(baz[2])='goodbye'])]")) % 23 % 38; +	CHECK_XPATH_NODESET(c, STR("foo[(bar[2][(baz[2])='goodbye'])]")) % 38; +} + +TEST_XML(xpath_xalan_predicate_9, "<doc><a><asub><asubsub/></asub></a><b><bsub><foo><child/></foo></bsub></b><c>f-inside</c><d><dsub><dsubsub><foundnode/></dsubsub></dsub></d><e>f-inside<esub>f-inside</esub><esubsib>f-inside</esubsib>f-inside</e><f><fsub/></f></doc>") +{ +	CHECK_XPATH_NODESET(doc, STR("doc/*[starts-with(name(.),'f')]")) % 23; +	CHECK_XPATH_NODESET(doc, STR("//*[starts-with(name(.),'f')]")) % 8 % 15 % 23 % 24; +} + +TEST_XML(xpath_xalan_predicate_10, "<doc><element1>Text from first element<child1>Text from child1 of first element</child1><child2>Text from child2 of first element</child2></element1><element2>Text from second element<child1>Text from child1 of second element</child1><child2 attr1='yes'>Text from child2 of second element (correct execution)</child2></element2></doc>") +{ +	xml_node c = doc.child(STR("doc")); + +	CHECK_XPATH_STRING(c, STR("//child2[ancestor::element2]"), STR("Text from child2 of second element (correct execution)")); +	CHECK_XPATH_STRING(c, STR("//child2[ancestor-or-self::element2]"), STR("Text from child2 of second element (correct execution)")); +	CHECK_XPATH_STRING(c, STR("//child2[attribute::attr1]"), STR("Text from child2 of second element (correct execution)")); +} + +TEST_XML(xpath_xalan_predicate_11, "<doc><a squish='heavy' squash='butternut'>1</a><a squish='heavy' squeesh='virus'>2</a><a squash='butternut' squeesh='virus'>3</a><a squish='heavy'>4</a><a squeesh='virus'>5</a><a squash='butternut'>6</a></doc>") +{ +	xml_node c = doc.child(STR("doc")); + +	CHECK_XPATH_NODESET(c, STR("a[@squeesh or (@squish and @squash)]")) % 3 % 7 % 11 % 18; +	CHECK_XPATH_NODESET(c, STR("a[(@squeesh or @squish) and @squash]")) % 3 % 11; +	CHECK_XPATH_NODESET(c, STR("a[@squeesh or @squish and @squash]")) % 3 % 7 % 11 % 18; +} + +TEST_XML(xpath_xalan_predicate_12, "<doc><a>1</a><a>2<achild>target</achild></a><a>3</a><a>target</a></doc>") +{ +	CHECK_XPATH_STRING(doc, STR("doc/a[following-sibling::*=descendant::*]"), STR("2target")); +} + +TEST_XML(xpath_xalan_predicate_13, "<doc><a squish='heavy'>1</a><a>2<achild>target</achild></a><a>3</a></doc>") +{ +	CHECK_XPATH_NODESET(doc, STR("doc/a[('target'=descendant::*) or @squish]")) % 3 % 6; +	CHECK_XPATH_NODESET(doc, STR("doc/a[not(('target'=descendant::*) or @squish)]")) % 10; +} + +TEST_XML(xpath_xalan_predicate_14, "<doc><a squish='heavy'>1</a><a>2<achild size='large'>child2</achild></a><a>3</a><a attrib='present'>4<achild>child4</achild></a></doc>") +{ +	CHECK_XPATH_NODESET(doc, STR("doc/a[not(@*)]")) % 6 % 11; +} + +TEST_XML(xpath_xalan_predicate_15, "<doc><a><asub><asubsub/></asub></a><b><bsub>x</bsub></b><c>inside</c><d><dsub><q><foundnode/></q></dsub></d></doc>") +{ +	CHECK_XPATH_NODESET(doc, STR("doc/descendant::*[string-length(name(.))=1]")) % 3 % 6 % 9 % 11 % 13; +} + +#endif diff --git a/tests/test_xpath_xalan_5.cpp b/tests/test_xpath_xalan_5.cpp index 3a71bc2..e6a4fb9 100644 --- a/tests/test_xpath_xalan_5.cpp +++ b/tests/test_xpath_xalan_5.cpp @@ -1,293 +1,293 @@ -#ifndef PUGIXML_NO_XPATH
 -
 -#include "common.hpp"
 -
 -TEST_XML(xpath_xalan_select_1, "<doc><a><b attr='test'/></a><c><d><e/></d></c></doc>")
 -{
 -	CHECK_XPATH_STRING(doc, STR("/doc/a/b/@attr"), STR("test"));
 -}
 -
 -TEST_XML(xpath_xalan_select_2, "<doc><do do='-do-'>do</do><re>re</re><mi mi1='-mi1-' mi2='mi2'>mi</mi><fa fa='-fa-'>fa<so so='-so-'>so<la>la<ti>ti</ti>do</la></so></fa><Gsharp so='so+'>G#</Gsharp><Aflat><natural><la>A</la></natural>Ab</Aflat><Bflat>Bb</Bflat><Csharp><natural>C</natural>C#<doublesharp>D</doublesharp></Csharp></doc>")
 -{
 -	xml_node c = doc.child(STR("doc"));
 -
 -	// This should come out fasolatido:
 -	CHECK_XPATH_NODESET(c, STR("fa")) % 12;
 -	// This should come out doremifasolatido:
 -	CHECK_XPATH_NODESET(c, STR("mi | do | fa | re")) % 3 % 6 % 8 % 12;
 -	// This should come out do-do-remi-mi1-mi2fasolatido-fa--so-:
 -	CHECK_XPATH_NODESET(c, STR("mi[@mi2='mi2'] | do | fa/so/@so | fa | mi/@* | re | fa/@fa | do/@do")) % 3 % 4 % 6 % 8 % 9 % 10 % 12 % 13 % 16;
 -	// This should come out solatidoG#:
 -	CHECK_XPATH_NODESET(c, STR(".//*[@so]")) % 15 % 23;
 -	// This should come out relatidoABb:
 -	CHECK_XPATH_NODESET(c, STR("*//la | //Bflat | re")) % 6 % 18 % 28 % 31;
 -	// This should come out domitiACD:
 -	CHECK_XPATH_NODESET(c, STR("fa/../mi | Aflat/natural/la | Csharp//* | /doc/do | *//ti")) % 3 % 8 % 20 % 28 % 34 % 37;
 -}
 -
 -TEST_XML(xpath_xalan_select_3, "<doc><sub1><child1>preceding sibling number 1</child1><child2>current node</child2><child3>following sibling number 3</child3></sub1><sub2><c>cousin 1</c><c>cousin 2</c><child3>cousin 3</child3></sub2></doc>")
 -{
 -	CHECK_XPATH_NODESET(doc.child(STR("doc")).child(STR("sub1")).child(STR("child2")), STR("preceding-sibling::child1|//child3")) % 4 % 8 % 15;
 -}
 -
 -TEST_XML(xpath_xalan_select_4, "<doc><child>bad1<sub>bad2</sub></child><c>bad3<sub>bad4</sub></c><sub>OK<nogo>bad5</nogo></sub></doc>")
 -{
 -	xml_node c = doc.child(STR("doc"));
 -
 -	CHECK_XPATH_NODESET(c, STR("child::sub")) % 11;
 -	CHECK_XPATH_NODESET(c, STR("child ::sub")) % 11;
 -	CHECK_XPATH_NODESET(c, STR("child:: sub")) % 11;
 -	CHECK_XPATH_NODESET(c, STR("child :: sub")) % 11;
 -}
 -
 -TEST_XML_FLAGS(xpath_xalan_select_5, "<doc>bad0<!-- Good --><comment>bad1<sub>bad2</sub></comment></doc>", parse_default | parse_comments)
 -{
 -	xml_node c = doc.child(STR("doc"));
 -
 -	CHECK_XPATH_NODESET(c, STR("comment()")) % 4;
 -	CHECK_XPATH_NODESET(c, STR("comment ()")) % 4;
 -	CHECK_XPATH_NODESET(c, STR("comment ( ) ")) % 4;
 -	CHECK_XPATH_NUMBER(c, STR("string-length()"), 12);
 -	CHECK_XPATH_NUMBER(c, STR("string-length ()"), 12);
 -	CHECK_XPATH_NUMBER(c, STR("string-length ( ) "), 12);
 -}
 -
 -TEST_XML(xpath_xalan_select_6, "<div div='20' div-5='12'>9</div>")
 -{
 -	xml_node c = doc.child(STR("div"));
 -
 -	CHECK_XPATH_NUMBER(doc, STR("div +3"), 12);
 -	CHECK_XPATH_NUMBER(doc, STR("* +3"), 12);
 -	CHECK_XPATH_NUMBER(c, STR("@div - 5"), 15);
 -	CHECK_XPATH_NUMBER(c, STR("@div -5"), 15);
 -	CHECK_XPATH_NUMBER(c, STR("@div-5"), 12);
 -	CHECK_XPATH_NUMBER(c, STR("@*-5"), 15);
 -	CHECK_XPATH_NUMBER(doc, STR("16-div"), 7);
 -	CHECK_XPATH_NUMBER(doc, STR("25-*"), 16);
 -	CHECK_XPATH_NUMBER(doc, STR("54 div*"), 6);
 -	CHECK_XPATH_NUMBER(doc, STR("(* - 4) div 2"), 2.5);
 -	CHECK_XPATH_NUMBER(doc, STR("' 6 ' div 2"), 3);
 -	CHECK_XPATH_NUMBER(doc, STR("' 6 '*div"), 54);
 -	CHECK_XPATH_NUMBER(doc, STR("5.*."), 45);
 -	CHECK_XPATH_NUMBER(doc, STR("5.+."), 14);
 -}
 -
 -TEST_XML(xpath_xalan_select_7, "<doc div='20'><div>9</div><attribute>8</attribute></doc>")
 -{
 -	xml_node c = doc.child(STR("doc"));
 -
 -	CHECK_XPATH_NUMBER(c, STR("attribute :: div"), 20);
 -	CHECK_XPATH_NUMBER(c, STR("attribute :: *"), 20);
 -	CHECK_XPATH_NUMBER(c, STR("attribute*(div - 4)"), 40);
 -	CHECK_XPATH_NUMBER(c, STR("(* - 4)**"), 45);
 -}
 -
 -TEST_XML(xpath_xalan_select_8, "<doc><a>x<div>7</div></a><a>y<div>9</div></a><a>z<div>5</div></a></doc>")
 -{
 -	CHECK_XPATH_NODESET(doc, STR("doc/a[div=9]")) % 7;
 -}
 -
 -TEST_XML(xpath_xalan_select_9, "<doc><a s='v'><b>7</b><c>3</c></a><a s='w'><b>7</b><c>9</c></a><a s='x'><b>9</b><c>2</c></a><a s='y'><b>9</b><c>9</c></a><a s='z'><b>2</b><c>0</c></a></doc>")
 -{
 -	CHECK_XPATH_NODESET(doc, STR("doc/a[*=9]")) % 9 % 15 % 21;
 -}
 -
 -TEST_XML(xpath_xalan_select_10, "<doc><sub1><child1>child1</child1></sub1><sub2><child2>child2</child2></sub2><sub3><child3/></sub3></doc>")
 -{
 -	CHECK_XPATH_NODESET(doc, STR("/doc/sub1/child1|/doc/sub2/child2")) % 4 % 7;
 -	CHECK_XPATH_NODESET(doc.child(STR("doc")), STR("sub1/child1|/doc/sub2/child2")) % 4 % 7;
 -	CHECK_XPATH_NODESET(doc.child(STR("doc")), STR("sub1/child1|sub2/child2")) % 4 % 7;
 -	CHECK_XPATH_NODESET(doc, STR("//self::child1|//self::child2")) % 4 % 7;
 -	CHECK_XPATH_NODESET(doc, STR("//child1|//child2")) % 4 % 7;
 -	CHECK_XPATH_NODESET(doc, STR("//child1|//child2|//child3")) % 4 % 7 % 10;
 -}
 -
 -TEST_XML(xpath_xalan_select_11, "<doc><sub1 pos='1'><child1>descendant number 1</child1></sub1><sub2 pos='2'><child1>descendant number 2</child1></sub2></doc>")
 -{
 -	CHECK_XPATH_NODESET(doc, STR("//child1/ancestor::sub1|//child1/ancestor::sub2")) % 3 % 7;
 -}
 -
 -TEST_XML(xpath_xalan_select_12, "<doc><sub pos='1'><child>child number 1</child><sub-sub pos='1sub'><child>grandchild number 1</child></sub-sub></sub><sub0 pos='2-no'><child>child number 2</child><sub pos='2.5'><child>grandchild number 2</child></sub></sub0><sub pos='3'><child>child number 3</child><subno pos='3.5-no'><child>grandchild number 3</child></subno></sub><sub0 pos='4-no'><child>child number 4</child><sub-sub pos='4sub'><child>grandchild number 4</child></sub-sub></sub0></doc>")
 -{
 -    CHECK_XPATH_NODESET(doc, STR("//child/ancestor-or-self::sub | //child/ancestor-or-self::sub-sub")) % 3 % 7 % 15 % 19 % 31;
 -}
 -
 -TEST_XML(xpath_xalan_select_13, "<doc><book><author><name real='no'>Carmelo Montanez</name><chapters>Nine</chapters><bibliography></bibliography></author></book><book><author><name real='na'>David Marston</name><chapters>Seven</chapters><bibliography></bibliography></author></book><book><author><name real='yes'>Mary Brady</name><chapters>Ten</chapters><bibliography><author><name>Lynne Rosenthal</name><chapters>Five</chapters></author></bibliography></author></book></doc>")
 -{
 -	CHECK_XPATH_NODESET(doc, STR("doc/book/author[name/@real='no']|doc/book/author[name/@real='yes']")) % 4 % 20;
 -	CHECK_XPATH_NODESET(doc, STR("doc/book/author[(name/@real='no' and position()=1)]|doc/book/author[(name/@real='yes' and position()=last())]")) % 4 % 20;
 -	CHECK_XPATH_NODESET(doc, STR("doc/book/author[name='Mary Brady']|doc/book/author[name/@real='no']")) % 4 % 20;
 -	CHECK_XPATH_NODESET(doc, STR("doc/book/author/name|doc/book/author/bibliography/author/name")) % 5 % 13 % 21 % 28;
 -	CHECK_XPATH_NODESET(doc, STR("doc/book/author/name|doc/book/author/bibliography/author/chapters")) % 5 % 13 % 21 % 30;
 -	CHECK_XPATH_NODESET(doc, STR("doc/book/author/name|doc/book/author/noElement")) % 5 % 13 % 21;
 -	CHECK_XPATH_NODESET(doc, STR("//noChild1|//noChild2"));
 -}
 -
 -TEST_XML(xpath_xalan_select_14, "<doc><sub1 pos='1'><child1>child number 1</child1></sub1><sub2 pos='2'><child2>child number 2</child2></sub2><sub3/></doc>")
 -{
 -	xml_node c = doc.child(STR("doc"));
 -
 -	CHECK_XPATH_NODESET(c, STR("child::sub1|child::sub2")) % 3 % 7;
 -	CHECK_XPATH_NODESET(c, STR("descendant::child1|descendant::child2")) % 5 % 9;
 -	CHECK_XPATH_NODESET(c, STR("descendant-or-self::sub1|descendant-or-self::sub2")) % 3 % 7;
 -	CHECK_XPATH_NODESET(c.child(STR("sub2")), STR("preceding-sibling::sub1|following-sibling::sub3")) % 3 % 11;
 -}
 -
 -TEST_XML(xpath_xalan_select_15, "<doc><child>Selection of this child is an error.</child><child high='3'>Selection of this child is an error.</child><child wide='4'>Selection of this child is an error.</child><child wide='4' high='3'>Selection of this child is an error.</child><child wide='3'>E</child><child wide='3' high='3'>F</child><child wide='3' deep='3'>G</child><child wide='4' deep='2'>Selection of this child is an error.</child><child wide='4' deep='2' high='3'>Selection of this child is an error.</child><child wide='3' deep='2'>J</child><child wide='3' deep='3' high='3'>K</child><child deep='2'>Selection of this child is an error.</child><child deep='2' high='3'>Selection of this child is an error.</child><child deep='3'>N</child><child deep='3' high='3'>O</child><child wide='4' deep='3'>P</child></doc>")
 -{
 -	xml_node c = doc.child(STR("doc"));
 -
 -	CHECK_XPATH_NODESET(c, STR("child[@wide='3']|child[@deep='3']")) % 15 % 18 % 22 % 35 % 39 % 51 % 54 % 58;
 -	CHECK_XPATH_NODESET(c, STR("child[@deep='3']|child[@wide='3']")) % 15 % 18 % 22 % 35 % 39 % 51 % 54 % 58;
 -}
 -
 -TEST_XML(xpath_xalan_select_16, "<doc><a squish='light' squash='butternut'>1</a><a squeesh='' squish='extreme'>2</a><a squash='butternut' squeesh=''>3</a><a squish='heavy' squash='sport' squeesh=''>4</a></doc>")
 -{
 -	CHECK_XPATH_NUMBER(doc, STR("count(doc/a/attribute::*)"), 9);
 -	CHECK_XPATH_NUMBER(doc, STR("count(//@*)"), 9);
 -	CHECK_XPATH_NUMBER(doc, STR("count(//@squish)"), 3);
 -}
 -
 -TEST_XML(xpath_xalan_select_17, "<directions><north><dup1/><dup2/><south/><east/><west/></north><north1/><north2><dup1/><dup2/><dup3/><dup4/></north2><north3><dup1/><dup2/><south-north/><east-north/><west-north/></north3><south/><east><dup1/><dup2/><north-east/><south-east/><west-east/></east><west/></directions>")
 -{
 -	xml_node c = doc.child(STR("directions"));
 -
 -    CHECK_XPATH_NODESET(c, STR("north/* | north/dup1 | north/dup2")) % 4 % 5 % 6 % 7 % 8;
 -    CHECK_XPATH_NODESET(c, STR("north/dup2 | north/dup1 | north/*")) % 4 % 5 % 6 % 7 % 8;
 -    CHECK_XPATH_NODESET(c, STR("//north/dup2 | south/preceding-sibling::*[4]/* | north/dup1 | north/*")) % 4 % 5 % 6 % 7 % 8;
 -    CHECK_XPATH_NODESET(c, STR("north/dup2 | south/preceding-sibling::*[4]/* | north/*")) % 4 % 5 % 6 % 7 % 8;
 -}
 -
 -TEST_XML(xpath_xalan_select_18, "<para><font color='red'>Hello</font><font color='green'>There</font><font color='blue'>World</font></para>")
 -{
 -    CHECK_XPATH_NODESET(doc, STR("/para/font[@color='green']")) % 6;
 -    CHECK_XPATH_NODESET(doc.child(STR("para")), STR("/para/font[@color='green']")) % 6;
 -    CHECK_XPATH_NODESET(doc.child(STR("para")).last_child(), STR("/para/font[@color='green']")) % 6;
 -}
 -
 -TEST_XML_FLAGS(xpath_xalan_select_19, "<doc>1<a>in-a</a>2<!-- upper comment --><b>3<bb>4<bbb>5</bbb>6</bb>7</b><!-- lower comment -->8<c>in-c</c>9<?pi?></doc>", parse_default | parse_comments | parse_pi)
 -{
 -	CHECK_XPATH_NODESET(doc, STR("//*")) % 2 % 4 % 8 % 10 % 12 % 18;
 -	CHECK_XPATH_NODESET(doc, STR("//node()")) % 2 % 3 % 4 % 5 % 6 % 7 % 8 % 9 % 10 % 11 % 12 % 13 % 14 % 15 % 16 % 17 % 18 % 19 % 20 % 21;
 -	CHECK_XPATH_NODESET(doc, STR("//text()")) % 3 % 5 % 6 % 9 % 11 % 13 % 14 % 15 % 17 % 19 % 20;
 -	CHECK_XPATH_NODESET(doc, STR("//comment()")) % 7 % 16;
 -	CHECK_XPATH_NODESET(doc, STR("//processing-instruction()")) % 21;
 -}
 -
 -TEST_XML(xpath_xalan_bugzilla_1, "<report><colData colId='F'>1</colData><colData colId='L'>5</colData><colData colId='F'>1</colData><colData colId='L'>5</colData><colData colId='L'>2</colData><colData colId='F'>2</colData><colData colId='L'>5</colData><colData colId='F'>2</colData></report>")
 -{
 -	CHECK_XPATH_NODESET(doc, STR("/report/colData[@colId='F' and not(.=preceding::colData)]")) % 3;
 -}
 -
 -TEST(xpath_xalan_error_boolean)
 -{
 -	CHECK_XPATH_FAIL(STR("nt(true())"));
 -	CHECK_XPATH_FAIL(STR("not(troo())"));
 -	CHECK_XPATH_FAIL(STR("troo() and (2 = 2)"));
 -	CHECK_XPATH_FAIL(STR("troo() or (2 = 2)"));
 -	CHECK_XPATH_FAIL(STR("2 = troo()"));
 -	CHECK_XPATH_FAIL(STR("boolean(troo())"));
 -	CHECK_XPATH_FAIL(STR("true(doc)"));
 -	CHECK_XPATH_FAIL(STR("false(doc)"));
 -	CHECK_XPATH_FAIL(STR("not()"));
 -	CHECK_XPATH_FAIL(STR("not(false(), doc)"));
 -	CHECK_XPATH_FAIL(STR("boolean()"));
 -	CHECK_XPATH_FAIL(STR("boolean(false(), doc)"));
 -	CHECK_XPATH_FAIL(STR("lang()"));
 -	CHECK_XPATH_FAIL(STR("lang('en','us')"));
 -}
 -
 -TEST(xpath_xalan_error_conditional)
 -{
 -	CHECK_XPATH_FAIL(STR(""));
 -	CHECK_XPATH_FAIL(STR("@name='John' | @name='Joe'"));
 -	CHECK_XPATH_FAIL(STR("\x95not(name(.)='')"));
 -}
 -
 -TEST(xpath_xalan_error_match)
 -{
 -	CHECK_XPATH_FAIL(STR("//"));
 -	CHECK_XPATH_FAIL(STR("section1|"));
 -	CHECK_XPATH_FAIL(STR("|section1"));
 -}
 -
 -TEST(xpath_xalan_error_math)
 -{
 -	CHECK_XPATH_FAIL(STR("6 quo 4"));
 -	CHECK_XPATH_FAIL(STR("-troo()"));
 -	CHECK_XPATH_FAIL(STR("number(troo())"));
 -	CHECK_XPATH_FAIL(STR("5 * troo()"));
 -	CHECK_XPATH_FAIL(STR("12 div troo()"));
 -	CHECK_XPATH_FAIL(STR("number(8,doc)"));
 -	CHECK_XPATH_FAIL(STR("sum(doc, 8)"));
 -	CHECK_XPATH_FAIL(STR("sum()"));
 -	CHECK_XPATH_FAIL(STR("floor(8,7)"));
 -	CHECK_XPATH_FAIL(STR("floor()"));
 -	CHECK_XPATH_FAIL(STR("ceiling(8,7)"));
 -	CHECK_XPATH_FAIL(STR("ceiling()"));
 -	CHECK_XPATH_FAIL(STR("round(8,7)"));
 -	CHECK_XPATH_FAIL(STR("round()"));
 -}
 -
 -TEST(xpath_xalan_error_namespace)
 -{
 -	CHECK_XPATH_FAIL(STR("local-name(baz2:b,..)"));
 -	CHECK_XPATH_FAIL(STR("namespace-uri(baz2:b,..)"));
 -	CHECK_XPATH_FAIL(STR("name(a,b)"));
 -	CHECK_XPATH_FAIL(STR(":foo"));
 -	CHECK_XPATH_FAIL(STR("*:foo"));
 -}
 -
 -TEST(xpath_xalan_error_position)
 -{
 -	CHECK_XPATH_FAIL(STR("*[last(*,2)]"));
 -	CHECK_XPATH_FAIL(STR("position(b)=1"));
 -	CHECK_XPATH_FAIL(STR("count()"));
 -	CHECK_XPATH_FAIL(STR("count(*,4)"));
 -	CHECK_XPATH_FAIL(STR("position()=last(a)"));
 -}
 -
 -TEST(xpath_xalan_error_select)
 -{
 -	CHECK_XPATH_FAIL(STR(""));
 -	CHECK_XPATH_FAIL(STR("count(troo())"));
 -	CHECK_XPATH_FAIL(STR("c::sub"));
 -	CHECK_XPATH_FAIL(STR("c()"));
 -	CHECK_XPATH_FAIL(STR("(* - 4) foo 2"));
 -	CHECK_XPATH_FAIL(STR("5 . + *"));
 -	CHECK_XPATH_FAIL(STR("4/."));
 -	CHECK_XPATH_FAIL(STR("true()/."));
 -	CHECK_XPATH_FAIL(STR("item//[@type='x']"));
 -	CHECK_XPATH_FAIL(STR("//"));
 -	CHECK_XPATH_FAIL(STR("item//"));
 -	CHECK_XPATH_FAIL(STR("count(//)"));
 -	CHECK_XPATH_FAIL(STR("substring-after(//,'0')"));
 -	CHECK_XPATH_FAIL(STR("//+17"));
 -	CHECK_XPATH_FAIL(STR("//|subitem"));
 -	CHECK_XPATH_FAIL(STR("..[near-north]"));
 -}
 -
 -TEST(xpath_xalan_error_string)
 -{
 -	CHECK_XPATH_FAIL(STR("string(troo())"));
 -	CHECK_XPATH_FAIL(STR("string-length(troo())"));
 -	CHECK_XPATH_FAIL(STR("normalize-space(a,'\t\r\n ab    cd  ')"));
 -	CHECK_XPATH_FAIL(STR("contains('ENCYCLOPEDIA')"));
 -	CHECK_XPATH_FAIL(STR("contains('ENCYCLOPEDIA','LOPE',doc)"));
 -	CHECK_XPATH_FAIL(STR("starts-with('ENCYCLOPEDIA')"));
 -	CHECK_XPATH_FAIL(STR("starts-with('ENCYCLOPEDIA','LOPE',doc)"));
 -	CHECK_XPATH_FAIL(STR("substring-before('ENCYCLOPEDIA')"));
 -	CHECK_XPATH_FAIL(STR("substring-before('ENCYCLOPEDIA','LOPE',doc)"));
 -	CHECK_XPATH_FAIL(STR("substring-after('ENCYCLOPEDIA')"));
 -	CHECK_XPATH_FAIL(STR("substring-after('ENCYCLOPEDIA','LOPE',doc)"));
 -	CHECK_XPATH_FAIL(STR("substring('ENCYCLOPEDIA')"));
 -	CHECK_XPATH_FAIL(STR("substring('ENCYCLOPEDIA',4,5,2)"));
 -	CHECK_XPATH_FAIL(STR("concat('x')"));
 -	CHECK_XPATH_FAIL(STR("string-length('ENCYCLOPEDIA','PEDI')"));
 -	CHECK_XPATH_FAIL(STR("translate('bar','abc')"));
 -	CHECK_XPATH_FAIL(STR("translate('bar','abc','ABC','output')"));
 -	CHECK_XPATH_FAIL(STR("string(22,44)"));
 -	CHECK_XPATH_FAIL(STR("concat(/*)"));
 -}
 -
 -#endif
 +#ifndef PUGIXML_NO_XPATH + +#include "common.hpp" + +TEST_XML(xpath_xalan_select_1, "<doc><a><b attr='test'/></a><c><d><e/></d></c></doc>") +{ +	CHECK_XPATH_STRING(doc, STR("/doc/a/b/@attr"), STR("test")); +} + +TEST_XML(xpath_xalan_select_2, "<doc><do do='-do-'>do</do><re>re</re><mi mi1='-mi1-' mi2='mi2'>mi</mi><fa fa='-fa-'>fa<so so='-so-'>so<la>la<ti>ti</ti>do</la></so></fa><Gsharp so='so+'>G#</Gsharp><Aflat><natural><la>A</la></natural>Ab</Aflat><Bflat>Bb</Bflat><Csharp><natural>C</natural>C#<doublesharp>D</doublesharp></Csharp></doc>") +{ +	xml_node c = doc.child(STR("doc")); + +	// This should come out fasolatido: +	CHECK_XPATH_NODESET(c, STR("fa")) % 12; +	// This should come out doremifasolatido: +	CHECK_XPATH_NODESET(c, STR("mi | do | fa | re")) % 3 % 6 % 8 % 12; +	// This should come out do-do-remi-mi1-mi2fasolatido-fa--so-: +	CHECK_XPATH_NODESET(c, STR("mi[@mi2='mi2'] | do | fa/so/@so | fa | mi/@* | re | fa/@fa | do/@do")) % 3 % 4 % 6 % 8 % 9 % 10 % 12 % 13 % 16; +	// This should come out solatidoG#: +	CHECK_XPATH_NODESET(c, STR(".//*[@so]")) % 15 % 23; +	// This should come out relatidoABb: +	CHECK_XPATH_NODESET(c, STR("*//la | //Bflat | re")) % 6 % 18 % 28 % 31; +	// This should come out domitiACD: +	CHECK_XPATH_NODESET(c, STR("fa/../mi | Aflat/natural/la | Csharp//* | /doc/do | *//ti")) % 3 % 8 % 20 % 28 % 34 % 37; +} + +TEST_XML(xpath_xalan_select_3, "<doc><sub1><child1>preceding sibling number 1</child1><child2>current node</child2><child3>following sibling number 3</child3></sub1><sub2><c>cousin 1</c><c>cousin 2</c><child3>cousin 3</child3></sub2></doc>") +{ +	CHECK_XPATH_NODESET(doc.child(STR("doc")).child(STR("sub1")).child(STR("child2")), STR("preceding-sibling::child1|//child3")) % 4 % 8 % 15; +} + +TEST_XML(xpath_xalan_select_4, "<doc><child>bad1<sub>bad2</sub></child><c>bad3<sub>bad4</sub></c><sub>OK<nogo>bad5</nogo></sub></doc>") +{ +	xml_node c = doc.child(STR("doc")); + +	CHECK_XPATH_NODESET(c, STR("child::sub")) % 11; +	CHECK_XPATH_NODESET(c, STR("child ::sub")) % 11; +	CHECK_XPATH_NODESET(c, STR("child:: sub")) % 11; +	CHECK_XPATH_NODESET(c, STR("child :: sub")) % 11; +} + +TEST_XML_FLAGS(xpath_xalan_select_5, "<doc>bad0<!-- Good --><comment>bad1<sub>bad2</sub></comment></doc>", parse_default | parse_comments) +{ +	xml_node c = doc.child(STR("doc")); + +	CHECK_XPATH_NODESET(c, STR("comment()")) % 4; +	CHECK_XPATH_NODESET(c, STR("comment ()")) % 4; +	CHECK_XPATH_NODESET(c, STR("comment ( ) ")) % 4; +	CHECK_XPATH_NUMBER(c, STR("string-length()"), 12); +	CHECK_XPATH_NUMBER(c, STR("string-length ()"), 12); +	CHECK_XPATH_NUMBER(c, STR("string-length ( ) "), 12); +} + +TEST_XML(xpath_xalan_select_6, "<div div='20' div-5='12'>9</div>") +{ +	xml_node c = doc.child(STR("div")); + +	CHECK_XPATH_NUMBER(doc, STR("div +3"), 12); +	CHECK_XPATH_NUMBER(doc, STR("* +3"), 12); +	CHECK_XPATH_NUMBER(c, STR("@div - 5"), 15); +	CHECK_XPATH_NUMBER(c, STR("@div -5"), 15); +	CHECK_XPATH_NUMBER(c, STR("@div-5"), 12); +	CHECK_XPATH_NUMBER(c, STR("@*-5"), 15); +	CHECK_XPATH_NUMBER(doc, STR("16-div"), 7); +	CHECK_XPATH_NUMBER(doc, STR("25-*"), 16); +	CHECK_XPATH_NUMBER(doc, STR("54 div*"), 6); +	CHECK_XPATH_NUMBER(doc, STR("(* - 4) div 2"), 2.5); +	CHECK_XPATH_NUMBER(doc, STR("' 6 ' div 2"), 3); +	CHECK_XPATH_NUMBER(doc, STR("' 6 '*div"), 54); +	CHECK_XPATH_NUMBER(doc, STR("5.*."), 45); +	CHECK_XPATH_NUMBER(doc, STR("5.+."), 14); +} + +TEST_XML(xpath_xalan_select_7, "<doc div='20'><div>9</div><attribute>8</attribute></doc>") +{ +	xml_node c = doc.child(STR("doc")); + +	CHECK_XPATH_NUMBER(c, STR("attribute :: div"), 20); +	CHECK_XPATH_NUMBER(c, STR("attribute :: *"), 20); +	CHECK_XPATH_NUMBER(c, STR("attribute*(div - 4)"), 40); +	CHECK_XPATH_NUMBER(c, STR("(* - 4)**"), 45); +} + +TEST_XML(xpath_xalan_select_8, "<doc><a>x<div>7</div></a><a>y<div>9</div></a><a>z<div>5</div></a></doc>") +{ +	CHECK_XPATH_NODESET(doc, STR("doc/a[div=9]")) % 7; +} + +TEST_XML(xpath_xalan_select_9, "<doc><a s='v'><b>7</b><c>3</c></a><a s='w'><b>7</b><c>9</c></a><a s='x'><b>9</b><c>2</c></a><a s='y'><b>9</b><c>9</c></a><a s='z'><b>2</b><c>0</c></a></doc>") +{ +	CHECK_XPATH_NODESET(doc, STR("doc/a[*=9]")) % 9 % 15 % 21; +} + +TEST_XML(xpath_xalan_select_10, "<doc><sub1><child1>child1</child1></sub1><sub2><child2>child2</child2></sub2><sub3><child3/></sub3></doc>") +{ +	CHECK_XPATH_NODESET(doc, STR("/doc/sub1/child1|/doc/sub2/child2")) % 4 % 7; +	CHECK_XPATH_NODESET(doc.child(STR("doc")), STR("sub1/child1|/doc/sub2/child2")) % 4 % 7; +	CHECK_XPATH_NODESET(doc.child(STR("doc")), STR("sub1/child1|sub2/child2")) % 4 % 7; +	CHECK_XPATH_NODESET(doc, STR("//self::child1|//self::child2")) % 4 % 7; +	CHECK_XPATH_NODESET(doc, STR("//child1|//child2")) % 4 % 7; +	CHECK_XPATH_NODESET(doc, STR("//child1|//child2|//child3")) % 4 % 7 % 10; +} + +TEST_XML(xpath_xalan_select_11, "<doc><sub1 pos='1'><child1>descendant number 1</child1></sub1><sub2 pos='2'><child1>descendant number 2</child1></sub2></doc>") +{ +	CHECK_XPATH_NODESET(doc, STR("//child1/ancestor::sub1|//child1/ancestor::sub2")) % 3 % 7; +} + +TEST_XML(xpath_xalan_select_12, "<doc><sub pos='1'><child>child number 1</child><sub-sub pos='1sub'><child>grandchild number 1</child></sub-sub></sub><sub0 pos='2-no'><child>child number 2</child><sub pos='2.5'><child>grandchild number 2</child></sub></sub0><sub pos='3'><child>child number 3</child><subno pos='3.5-no'><child>grandchild number 3</child></subno></sub><sub0 pos='4-no'><child>child number 4</child><sub-sub pos='4sub'><child>grandchild number 4</child></sub-sub></sub0></doc>") +{ +    CHECK_XPATH_NODESET(doc, STR("//child/ancestor-or-self::sub | //child/ancestor-or-self::sub-sub")) % 3 % 7 % 15 % 19 % 31; +} + +TEST_XML(xpath_xalan_select_13, "<doc><book><author><name real='no'>Carmelo Montanez</name><chapters>Nine</chapters><bibliography></bibliography></author></book><book><author><name real='na'>David Marston</name><chapters>Seven</chapters><bibliography></bibliography></author></book><book><author><name real='yes'>Mary Brady</name><chapters>Ten</chapters><bibliography><author><name>Lynne Rosenthal</name><chapters>Five</chapters></author></bibliography></author></book></doc>") +{ +	CHECK_XPATH_NODESET(doc, STR("doc/book/author[name/@real='no']|doc/book/author[name/@real='yes']")) % 4 % 20; +	CHECK_XPATH_NODESET(doc, STR("doc/book/author[(name/@real='no' and position()=1)]|doc/book/author[(name/@real='yes' and position()=last())]")) % 4 % 20; +	CHECK_XPATH_NODESET(doc, STR("doc/book/author[name='Mary Brady']|doc/book/author[name/@real='no']")) % 4 % 20; +	CHECK_XPATH_NODESET(doc, STR("doc/book/author/name|doc/book/author/bibliography/author/name")) % 5 % 13 % 21 % 28; +	CHECK_XPATH_NODESET(doc, STR("doc/book/author/name|doc/book/author/bibliography/author/chapters")) % 5 % 13 % 21 % 30; +	CHECK_XPATH_NODESET(doc, STR("doc/book/author/name|doc/book/author/noElement")) % 5 % 13 % 21; +	CHECK_XPATH_NODESET(doc, STR("//noChild1|//noChild2")); +} + +TEST_XML(xpath_xalan_select_14, "<doc><sub1 pos='1'><child1>child number 1</child1></sub1><sub2 pos='2'><child2>child number 2</child2></sub2><sub3/></doc>") +{ +	xml_node c = doc.child(STR("doc")); + +	CHECK_XPATH_NODESET(c, STR("child::sub1|child::sub2")) % 3 % 7; +	CHECK_XPATH_NODESET(c, STR("descendant::child1|descendant::child2")) % 5 % 9; +	CHECK_XPATH_NODESET(c, STR("descendant-or-self::sub1|descendant-or-self::sub2")) % 3 % 7; +	CHECK_XPATH_NODESET(c.child(STR("sub2")), STR("preceding-sibling::sub1|following-sibling::sub3")) % 3 % 11; +} + +TEST_XML(xpath_xalan_select_15, "<doc><child>Selection of this child is an error.</child><child high='3'>Selection of this child is an error.</child><child wide='4'>Selection of this child is an error.</child><child wide='4' high='3'>Selection of this child is an error.</child><child wide='3'>E</child><child wide='3' high='3'>F</child><child wide='3' deep='3'>G</child><child wide='4' deep='2'>Selection of this child is an error.</child><child wide='4' deep='2' high='3'>Selection of this child is an error.</child><child wide='3' deep='2'>J</child><child wide='3' deep='3' high='3'>K</child><child deep='2'>Selection of this child is an error.</child><child deep='2' high='3'>Selection of this child is an error.</child><child deep='3'>N</child><child deep='3' high='3'>O</child><child wide='4' deep='3'>P</child></doc>") +{ +	xml_node c = doc.child(STR("doc")); + +	CHECK_XPATH_NODESET(c, STR("child[@wide='3']|child[@deep='3']")) % 15 % 18 % 22 % 35 % 39 % 51 % 54 % 58; +	CHECK_XPATH_NODESET(c, STR("child[@deep='3']|child[@wide='3']")) % 15 % 18 % 22 % 35 % 39 % 51 % 54 % 58; +} + +TEST_XML(xpath_xalan_select_16, "<doc><a squish='light' squash='butternut'>1</a><a squeesh='' squish='extreme'>2</a><a squash='butternut' squeesh=''>3</a><a squish='heavy' squash='sport' squeesh=''>4</a></doc>") +{ +	CHECK_XPATH_NUMBER(doc, STR("count(doc/a/attribute::*)"), 9); +	CHECK_XPATH_NUMBER(doc, STR("count(//@*)"), 9); +	CHECK_XPATH_NUMBER(doc, STR("count(//@squish)"), 3); +} + +TEST_XML(xpath_xalan_select_17, "<directions><north><dup1/><dup2/><south/><east/><west/></north><north1/><north2><dup1/><dup2/><dup3/><dup4/></north2><north3><dup1/><dup2/><south-north/><east-north/><west-north/></north3><south/><east><dup1/><dup2/><north-east/><south-east/><west-east/></east><west/></directions>") +{ +	xml_node c = doc.child(STR("directions")); + +    CHECK_XPATH_NODESET(c, STR("north/* | north/dup1 | north/dup2")) % 4 % 5 % 6 % 7 % 8; +    CHECK_XPATH_NODESET(c, STR("north/dup2 | north/dup1 | north/*")) % 4 % 5 % 6 % 7 % 8; +    CHECK_XPATH_NODESET(c, STR("//north/dup2 | south/preceding-sibling::*[4]/* | north/dup1 | north/*")) % 4 % 5 % 6 % 7 % 8; +    CHECK_XPATH_NODESET(c, STR("north/dup2 | south/preceding-sibling::*[4]/* | north/*")) % 4 % 5 % 6 % 7 % 8; +} + +TEST_XML(xpath_xalan_select_18, "<para><font color='red'>Hello</font><font color='green'>There</font><font color='blue'>World</font></para>") +{ +    CHECK_XPATH_NODESET(doc, STR("/para/font[@color='green']")) % 6; +    CHECK_XPATH_NODESET(doc.child(STR("para")), STR("/para/font[@color='green']")) % 6; +    CHECK_XPATH_NODESET(doc.child(STR("para")).last_child(), STR("/para/font[@color='green']")) % 6; +} + +TEST_XML_FLAGS(xpath_xalan_select_19, "<doc>1<a>in-a</a>2<!-- upper comment --><b>3<bb>4<bbb>5</bbb>6</bb>7</b><!-- lower comment -->8<c>in-c</c>9<?pi?></doc>", parse_default | parse_comments | parse_pi) +{ +	CHECK_XPATH_NODESET(doc, STR("//*")) % 2 % 4 % 8 % 10 % 12 % 18; +	CHECK_XPATH_NODESET(doc, STR("//node()")) % 2 % 3 % 4 % 5 % 6 % 7 % 8 % 9 % 10 % 11 % 12 % 13 % 14 % 15 % 16 % 17 % 18 % 19 % 20 % 21; +	CHECK_XPATH_NODESET(doc, STR("//text()")) % 3 % 5 % 6 % 9 % 11 % 13 % 14 % 15 % 17 % 19 % 20; +	CHECK_XPATH_NODESET(doc, STR("//comment()")) % 7 % 16; +	CHECK_XPATH_NODESET(doc, STR("//processing-instruction()")) % 21; +} + +TEST_XML(xpath_xalan_bugzilla_1, "<report><colData colId='F'>1</colData><colData colId='L'>5</colData><colData colId='F'>1</colData><colData colId='L'>5</colData><colData colId='L'>2</colData><colData colId='F'>2</colData><colData colId='L'>5</colData><colData colId='F'>2</colData></report>") +{ +	CHECK_XPATH_NODESET(doc, STR("/report/colData[@colId='F' and not(.=preceding::colData)]")) % 3; +} + +TEST(xpath_xalan_error_boolean) +{ +	CHECK_XPATH_FAIL(STR("nt(true())")); +	CHECK_XPATH_FAIL(STR("not(troo())")); +	CHECK_XPATH_FAIL(STR("troo() and (2 = 2)")); +	CHECK_XPATH_FAIL(STR("troo() or (2 = 2)")); +	CHECK_XPATH_FAIL(STR("2 = troo()")); +	CHECK_XPATH_FAIL(STR("boolean(troo())")); +	CHECK_XPATH_FAIL(STR("true(doc)")); +	CHECK_XPATH_FAIL(STR("false(doc)")); +	CHECK_XPATH_FAIL(STR("not()")); +	CHECK_XPATH_FAIL(STR("not(false(), doc)")); +	CHECK_XPATH_FAIL(STR("boolean()")); +	CHECK_XPATH_FAIL(STR("boolean(false(), doc)")); +	CHECK_XPATH_FAIL(STR("lang()")); +	CHECK_XPATH_FAIL(STR("lang('en','us')")); +} + +TEST(xpath_xalan_error_conditional) +{ +	CHECK_XPATH_FAIL(STR("")); +	CHECK_XPATH_FAIL(STR("@name='John' | @name='Joe'")); +	CHECK_XPATH_FAIL(STR("\x95not(name(.)='')")); +} + +TEST(xpath_xalan_error_match) +{ +	CHECK_XPATH_FAIL(STR("//")); +	CHECK_XPATH_FAIL(STR("section1|")); +	CHECK_XPATH_FAIL(STR("|section1")); +} + +TEST(xpath_xalan_error_math) +{ +	CHECK_XPATH_FAIL(STR("6 quo 4")); +	CHECK_XPATH_FAIL(STR("-troo()")); +	CHECK_XPATH_FAIL(STR("number(troo())")); +	CHECK_XPATH_FAIL(STR("5 * troo()")); +	CHECK_XPATH_FAIL(STR("12 div troo()")); +	CHECK_XPATH_FAIL(STR("number(8,doc)")); +	CHECK_XPATH_FAIL(STR("sum(doc, 8)")); +	CHECK_XPATH_FAIL(STR("sum()")); +	CHECK_XPATH_FAIL(STR("floor(8,7)")); +	CHECK_XPATH_FAIL(STR("floor()")); +	CHECK_XPATH_FAIL(STR("ceiling(8,7)")); +	CHECK_XPATH_FAIL(STR("ceiling()")); +	CHECK_XPATH_FAIL(STR("round(8,7)")); +	CHECK_XPATH_FAIL(STR("round()")); +} + +TEST(xpath_xalan_error_namespace) +{ +	CHECK_XPATH_FAIL(STR("local-name(baz2:b,..)")); +	CHECK_XPATH_FAIL(STR("namespace-uri(baz2:b,..)")); +	CHECK_XPATH_FAIL(STR("name(a,b)")); +	CHECK_XPATH_FAIL(STR(":foo")); +	CHECK_XPATH_FAIL(STR("*:foo")); +} + +TEST(xpath_xalan_error_position) +{ +	CHECK_XPATH_FAIL(STR("*[last(*,2)]")); +	CHECK_XPATH_FAIL(STR("position(b)=1")); +	CHECK_XPATH_FAIL(STR("count()")); +	CHECK_XPATH_FAIL(STR("count(*,4)")); +	CHECK_XPATH_FAIL(STR("position()=last(a)")); +} + +TEST(xpath_xalan_error_select) +{ +	CHECK_XPATH_FAIL(STR("")); +	CHECK_XPATH_FAIL(STR("count(troo())")); +	CHECK_XPATH_FAIL(STR("c::sub")); +	CHECK_XPATH_FAIL(STR("c()")); +	CHECK_XPATH_FAIL(STR("(* - 4) foo 2")); +	CHECK_XPATH_FAIL(STR("5 . + *")); +	CHECK_XPATH_FAIL(STR("4/.")); +	CHECK_XPATH_FAIL(STR("true()/.")); +	CHECK_XPATH_FAIL(STR("item//[@type='x']")); +	CHECK_XPATH_FAIL(STR("//")); +	CHECK_XPATH_FAIL(STR("item//")); +	CHECK_XPATH_FAIL(STR("count(//)")); +	CHECK_XPATH_FAIL(STR("substring-after(//,'0')")); +	CHECK_XPATH_FAIL(STR("//+17")); +	CHECK_XPATH_FAIL(STR("//|subitem")); +	CHECK_XPATH_FAIL(STR("..[near-north]")); +} + +TEST(xpath_xalan_error_string) +{ +	CHECK_XPATH_FAIL(STR("string(troo())")); +	CHECK_XPATH_FAIL(STR("string-length(troo())")); +	CHECK_XPATH_FAIL(STR("normalize-space(a,'\t\r\n ab    cd  ')")); +	CHECK_XPATH_FAIL(STR("contains('ENCYCLOPEDIA')")); +	CHECK_XPATH_FAIL(STR("contains('ENCYCLOPEDIA','LOPE',doc)")); +	CHECK_XPATH_FAIL(STR("starts-with('ENCYCLOPEDIA')")); +	CHECK_XPATH_FAIL(STR("starts-with('ENCYCLOPEDIA','LOPE',doc)")); +	CHECK_XPATH_FAIL(STR("substring-before('ENCYCLOPEDIA')")); +	CHECK_XPATH_FAIL(STR("substring-before('ENCYCLOPEDIA','LOPE',doc)")); +	CHECK_XPATH_FAIL(STR("substring-after('ENCYCLOPEDIA')")); +	CHECK_XPATH_FAIL(STR("substring-after('ENCYCLOPEDIA','LOPE',doc)")); +	CHECK_XPATH_FAIL(STR("substring('ENCYCLOPEDIA')")); +	CHECK_XPATH_FAIL(STR("substring('ENCYCLOPEDIA',4,5,2)")); +	CHECK_XPATH_FAIL(STR("concat('x')")); +	CHECK_XPATH_FAIL(STR("string-length('ENCYCLOPEDIA','PEDI')")); +	CHECK_XPATH_FAIL(STR("translate('bar','abc')")); +	CHECK_XPATH_FAIL(STR("translate('bar','abc','ABC','output')")); +	CHECK_XPATH_FAIL(STR("string(22,44)")); +	CHECK_XPATH_FAIL(STR("concat(/*)")); +} + +#endif diff --git a/tests/writer_string.cpp b/tests/writer_string.cpp index 878a103..f35b461 100644 --- a/tests/writer_string.cpp +++ b/tests/writer_string.cpp @@ -1,77 +1,77 @@ -#include "writer_string.hpp"
 -
 -#include "test.hpp"
 -
 -static bool test_narrow(const std::string& result, const char* expected, size_t length)
 -{
 -	// check result
 -	if (result != std::string(expected, expected + length)) return false;
 -
 -	// check comparison operator (incorrect implementation can theoretically early-out on zero terminators...)
 -	if (length > 0 && result == std::string(expected, expected + length - 1) + "?") return false;
 -
 -	return true;
 -}
 -
 -void xml_writer_string::write(const void* data, size_t size)
 -{
 -	contents += std::string(static_cast<const char*>(data), size);
 -}
 -
 -std::string xml_writer_string::as_narrow() const
 -{
 -	return contents;
 -}
 -
 -std::wstring xml_writer_string::as_wide() const
 -{
 -	CHECK(contents.size() % sizeof(wchar_t) == 0);
 -
 -	return std::wstring(reinterpret_cast<const wchar_t*>(contents.data()), contents.size() / sizeof(wchar_t));
 -}
 -
 -std::basic_string<pugi::char_t> xml_writer_string::as_string() const
 -{
 -#ifdef PUGIXML_WCHAR_MODE // to avoid "condition is always true" warning in BCC
 -	CHECK(contents.size() % sizeof(pugi::char_t) == 0);
 -#endif
 -
 -	return std::basic_string<pugi::char_t>(reinterpret_cast<const pugi::char_t*>(contents.data()), contents.size() / sizeof(pugi::char_t));
 -}
 -
 -std::string save_narrow(const pugi::xml_document& doc, unsigned int flags, pugi::xml_encoding encoding)
 -{
 -	xml_writer_string writer;
 -
 -	doc.save(writer, STR(""), flags, encoding);
 -
 -	return writer.as_narrow();
 -}
 -
 -bool test_save_narrow(const pugi::xml_document& doc, unsigned int flags, pugi::xml_encoding encoding, const char* expected, size_t length)
 -{
 -	return test_narrow(save_narrow(doc, flags, encoding), expected, length);
 -}
 -
 -std::string write_narrow(pugi::xml_node node, unsigned int flags, pugi::xml_encoding encoding)
 -{
 -	xml_writer_string writer;
 -
 -	node.print(writer, STR(""), flags, encoding);
 -
 -	return writer.as_narrow();
 -}
 -
 -bool test_write_narrow(pugi::xml_node node, unsigned int flags, pugi::xml_encoding encoding, const char* expected, size_t length)
 -{
 -	return test_narrow(write_narrow(node, flags, encoding), expected, length);
 -}
 -
 -std::wstring write_wide(pugi::xml_node node, unsigned int flags, pugi::xml_encoding encoding)
 -{
 -	xml_writer_string writer;
 -
 -	node.print(writer, STR(""), flags, encoding);
 -
 -	return writer.as_wide();
 -}
 +#include "writer_string.hpp" + +#include "test.hpp" + +static bool test_narrow(const std::string& result, const char* expected, size_t length) +{ +	// check result +	if (result != std::string(expected, expected + length)) return false; + +	// check comparison operator (incorrect implementation can theoretically early-out on zero terminators...) +	if (length > 0 && result == std::string(expected, expected + length - 1) + "?") return false; + +	return true; +} + +void xml_writer_string::write(const void* data, size_t size) +{ +	contents += std::string(static_cast<const char*>(data), size); +} + +std::string xml_writer_string::as_narrow() const +{ +	return contents; +} + +std::wstring xml_writer_string::as_wide() const +{ +	CHECK(contents.size() % sizeof(wchar_t) == 0); + +	return std::wstring(reinterpret_cast<const wchar_t*>(contents.data()), contents.size() / sizeof(wchar_t)); +} + +std::basic_string<pugi::char_t> xml_writer_string::as_string() const +{ +#ifdef PUGIXML_WCHAR_MODE // to avoid "condition is always true" warning in BCC +	CHECK(contents.size() % sizeof(pugi::char_t) == 0); +#endif + +	return std::basic_string<pugi::char_t>(reinterpret_cast<const pugi::char_t*>(contents.data()), contents.size() / sizeof(pugi::char_t)); +} + +std::string save_narrow(const pugi::xml_document& doc, unsigned int flags, pugi::xml_encoding encoding) +{ +	xml_writer_string writer; + +	doc.save(writer, STR(""), flags, encoding); + +	return writer.as_narrow(); +} + +bool test_save_narrow(const pugi::xml_document& doc, unsigned int flags, pugi::xml_encoding encoding, const char* expected, size_t length) +{ +	return test_narrow(save_narrow(doc, flags, encoding), expected, length); +} + +std::string write_narrow(pugi::xml_node node, unsigned int flags, pugi::xml_encoding encoding) +{ +	xml_writer_string writer; + +	node.print(writer, STR(""), flags, encoding); + +	return writer.as_narrow(); +} + +bool test_write_narrow(pugi::xml_node node, unsigned int flags, pugi::xml_encoding encoding, const char* expected, size_t length) +{ +	return test_narrow(write_narrow(node, flags, encoding), expected, length); +} + +std::wstring write_wide(pugi::xml_node node, unsigned int flags, pugi::xml_encoding encoding) +{ +	xml_writer_string writer; + +	node.print(writer, STR(""), flags, encoding); + +	return writer.as_wide(); +} diff --git a/tests/writer_string.hpp b/tests/writer_string.hpp index acf6318..390f93b 100644 --- a/tests/writer_string.hpp +++ b/tests/writer_string.hpp @@ -1,27 +1,27 @@ -#ifndef HEADER_TEST_WRITER_STRING_HPP
 -#define HEADER_TEST_WRITER_STRING_HPP
 -
 -#include "../src/pugixml.hpp"
 -
 -#include <string>
 -
 -struct xml_writer_string: public pugi::xml_writer
 -{
 -	std::string contents;
 -	
 -	virtual void write(const void* data, size_t size);
 -
 -	std::string as_narrow() const;
 -	std::wstring as_wide() const;
 -	std::basic_string<pugi::char_t> as_string() const;
 -};
 -
 -std::string save_narrow(const pugi::xml_document& doc, unsigned int flags, pugi::xml_encoding encoding);
 -bool test_save_narrow(const pugi::xml_document& doc, unsigned int flags, pugi::xml_encoding encoding, const char* expected, size_t length);
 -
 -std::string write_narrow(pugi::xml_node node, unsigned int flags, pugi::xml_encoding encoding);
 -bool test_write_narrow(pugi::xml_node node, unsigned int flags, pugi::xml_encoding encoding, const char* expected, size_t length);
 -
 -std::wstring write_wide(pugi::xml_node node, unsigned int flags, pugi::xml_encoding encoding);
 -
 -#endif
 +#ifndef HEADER_TEST_WRITER_STRING_HPP +#define HEADER_TEST_WRITER_STRING_HPP + +#include "../src/pugixml.hpp" + +#include <string> + +struct xml_writer_string: public pugi::xml_writer +{ +	std::string contents; +	 +	virtual void write(const void* data, size_t size); + +	std::string as_narrow() const; +	std::wstring as_wide() const; +	std::basic_string<pugi::char_t> as_string() const; +}; + +std::string save_narrow(const pugi::xml_document& doc, unsigned int flags, pugi::xml_encoding encoding); +bool test_save_narrow(const pugi::xml_document& doc, unsigned int flags, pugi::xml_encoding encoding, const char* expected, size_t length); + +std::string write_narrow(pugi::xml_node node, unsigned int flags, pugi::xml_encoding encoding); +bool test_write_narrow(pugi::xml_node node, unsigned int flags, pugi::xml_encoding encoding, const char* expected, size_t length); + +std::wstring write_wide(pugi::xml_node node, unsigned int flags, pugi::xml_encoding encoding); + +#endif  | 
