The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# Before `make install' is performed this script should be runnable with
# `make test'. After `make install' it should work as `perl test.pl'

######################### We start with some black magic to print on failure.

# Change 1..1 below to 1..last_test_to_print .
# (It may become useful if the test is moved to ./t subdirectory.)

BEGIN {
 # OK, we want to check THIS version, not some older one
 unshift @INC, qw(blib/lib blib/arch);

 require Win32::DriveInfo;

 $| = 1; print "1..11\n";
}
END {print "not ok 1\n" unless $loaded;}

use Cwd;

$loaded = 1;
print "ok 1\n";

$test_num = 2;

# platform/build
eval { ($MajorVersion, $MinorVersion, $BuildNumber,
 $PlatformId, $BuildStr) = Win32::DriveInfo::GetVersionEx() };
print(( ! $@ ? "" : "not " )."ok ".($test_num++)."\n");
undef $@;

$ok = 1;
if (defined &Win32::GetOSVersion) {
  eval { ($string, $major, $minor, $build, $id) = &Win32::GetOSVersion };

  if (!$@) {
    $build = $build & 0xffff if Win32::IsWin95();
    $string =~ s/^ *(.*?) *$/$1/;

    $ok =
    $major  eq $MajorVersion &&
    $minor  eq $MinorVersion &&
    $string eq $BuildStr     &&
    $id     eq $PlatformId   &&
    $build  eq $BuildNumber;
  } else {
    $ok = 0; undef $@;
  }
}

print(( $ok ? "" : "not " )."ok ".($test_num++)."\n");

eval { @drives = Win32::DriveInfo::DrivesInUse() };
print(( ! $@ ? "" : "not " )."ok ".($test_num++)."\n");
undef $@;

eval { @drives2 = grep Win32::DriveInfo::IsReady($_), ("C".."Z") };
print(( ! $@ ? "" : "not " )."ok ".($test_num++)."\n");
undef $@;

eval {
$dr1 = join "", map uc($_), grep {Win32::DriveInfo::DriveType($_) == 3} @drives;
$dr2 = join "", map uc($_), grep {Win32::DriveInfo::DriveType($_) == 3} @drives2;
};
$ok = ! $@ && $dr1 eq $dr2;
warn "Test $test_num fails if one of your fixed drives has no root (not formatted)\n"
  unless $ok;
print(( $ok ? "" : "not " )."ok ".($test_num++)."\n");
undef $@;


eval {
for (@drives2) { # drives that have root (fixed and loaded removable)
  my ($VolumeName,
      $VolumeSerialNumber,
      $MaximumComponentLength,
      $FileSystemName, @attr) = Win32::DriveInfo::VolumeInfo($_);
  $vol->{$_} = {
	"label"   => $VolumeName,
	"serial"  => $VolumeSerialNumber,
	"maxcomp" => $MaximumComponentLength,
	"fsys"    => $FileSystemName,
	"attrs"   => \@attr,
  };
}
};
print(( ! $@ ? "" : "not " )."ok ".($test_num++)."\n");
undef $@;

# drive-types
eval {
  for (@drives2) {
    $vol->{$_}{"type"} = Win32::DriveInfo::DriveType($_);
  }
};
print(( ! $@ ? "" : "not " )."ok ".($test_num++)."\n");
undef $@;

$ok = 1;
if (defined &Win32::FsType) {
# Win32::FsType is rather strange one - it checks "current drive"
# i.e. the root of the current directory

  my $dir = cwd;

  for (@drives2) {
    chdir "$_:\\";
    my ($fstype, $flags, $maxcomplen) = &Win32::FsType;

    if ($fstype ne $vol->{$_}{"fsys"} || $maxcomplen ne $vol->{$_}{"maxcomp"}) {
       $ok = 0; last;
    }
  }
  chdir $dir;
}
print(( $ok ? "" : "not " )."ok ".($test_num++)."\n");

# drives that give correct "free" value on dir command -
# fixed, CD ROM, RAM drives.
# Removable media (2) are not tested for not to hear unpleasant sounds.
# Network drives(type 4) seems like giving wrong values.
@drives3 = grep {$vol->{$_}{"type"} =~ /[356]/} @drives2;
# print "@drives3\n";

eval {
for (@drives3) { # drives that have root (fixed and loaded removable)
  $vol->{$_}{"free"} = (Win32::DriveInfo::DriveSpace($_))[6];
}
};
print(( ! $@ ? "" : "not " )."ok ".($test_num++)."\n");
undef $@;

$ok=0;
for (@drives3) {
  ($label, $serial, $free, $units) = dir_cmd($_);
#print "$label, $serial, $free, $units|".$vol->{$_}{"label"}.",".$vol->{$_}{"serial"}.",".$vol->{$_}{"free"}."\n";
  $label ||= ""; $serial ||= ""; $free ||= 0;

  $ok =
  $label  eq $vol->{$_}{"label"} &&
  (($serial eq $vol->{$_}{"serial"}) ||
  # god knows why 4DOS does not return serial for CD-ROMs
  # but command.com does
   ($serial eq "") ||
  # RAM drives
  # 4DOS returns 0000:0000 serial when command.com returns nothing
  # I'm sure this is just a misinterpretation
   ($serial eq "0000:0000" && $vol->{$_}{"serial"} eq "")
  ) &&
  ($units ne "bytes" || $free eq $vol->{$_}{"free"});

  if ($units ne "bytes") {
    print "Drive $_: `dir' does not return the exact free space\n";
  } else {
    print "Drive $_: `dir' and module returned ".( $ok ? "" : "not " )."the same\n";
  }

  $ok or last;
}
print(( $ok ? "" : "not " )."ok ".($test_num++)."\n");


sub dir_cmd {
  my $drive = shift;
  substr($drive,1)="";

  my $cmd = $ENV{COMSPEC} || "command.com";

  # cmd.exe + nmake sometimes give $? == -1
  # one case is - when I erroneously called dmake first
  # after that I had to reopen shell to get rid of this error
  my $out = `"$cmd" /c dir $drive:\\`;   # print "$?,".length($out)."\n";
  return if !$out || $out !~ /\S/ || $?;

  # label can contain spaces
  my $label;
  if      ($out =~ /Volume[\t ]+in[\t ]+drive[\t ]+$drive[\t ]+(is[\t ]+unlabeled|has[\t ]+no[\t ]+label)/i) {
      $label = "";
  } elsif ($out =~ /Volume[\t ]+in[\t ]+drive[\t ]+$drive[\t ]+is[\t ]*(.*?)([\t ]+Serial[\t ]+number[\t ]+is|[\t ]*$)/im) {
      $label = $1;
  } else {
      $label = "";
  }

  my ($serial) = ($out =~ /Serial[\t ]+number[\t ]+is[\t ]*(.*?)[\t ]*$/im) ? $1 : "";
  $serial =~ s/-/:/g; # command.com and cmd.exe use - delimiter

  # stupid command.com returns partial MB value on large drives
  # cmd.exe uses , or \xFF or God knows what separator
  my ($free, $units) = $out =~ /(\d[\d\W]+)(bytes|MB|GB)\s+free/i or return;
  $free  =~ s/\D//g;

  ($label, $serial, $free, $units);
}