012throwables.t 6.86 KB
Newer Older
1 2 3 4 5 6
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# This Source Code Form is "Incompatible With Secondary Licenses", as
# defined by the Mozilla Public License, v. 2.0.
7 8 9 10 11 12


##################
#Bugzilla Test 12#
######Errors######

13
use 5.10.1;
14
use strict;
15 16
use warnings;

17
use lib qw(. lib t);
18

19
use Bugzilla::Constants;
20 21
use Bugzilla::WebService::Constants;

22 23 24 25 26 27 28 29 30 31 32 33
use File::Spec;
use Support::Files;
use Support::Templates;
use Test::More;

my %Errors = ();

# Just a workaround for template errors handling. Define it as used.
push @{$Errors{code}{template_error}{used_in}{'Bugzilla/Error.pm'}}, 0;

# Define files to test. Each file would have a list of error messages, if any.
my %test_templates = ();
34
my %test_modules   = ();
35 36 37

# Find all modules
foreach my $module (@Support::Files::testitems) {
38
  $test_modules{$module} = ();
39 40 41 42 43 44 45
}

# Find all error templates
# Process all files since otherwise handling template hooks would became too
# hairy. But let us do it only once.

foreach my $include_path (@include_paths) {
46 47
  foreach my $path (@{$actual_files{$include_path}}) {
    my $file = File::Spec->catfile($include_path, $path);
48
    $file =~ s/\s.*$//;                 # nuke everything after the first space
49 50 51 52 53 54 55 56
    $file =~ s|\\|/|g if ON_WINDOWS;    # convert \ to / in path if on windows
    $test_templates{$file} = () if $file =~ m#global/(code|user)-error\.html\.tmpl#;

    # Make sure the extension is not disabled
    if ($file =~ m#^(extensions/[^/]+/)#) {
      $test_templates{$file} = ()
        if !-e "${1}disabled"
        && $file =~ m#global/(code|user)-error-errors\.html\.tmpl#;
57
    }
58
  }
59 60
}

61 62
# Count the tests. The +1 is for checking the WS_ERROR_CODE errors.
my $tests = (scalar keys %test_modules) + (scalar keys %test_templates) + 1;
63 64 65 66 67 68 69
exit 0 if !$tests;

# Set requested tests counter.
plan tests => $tests;

# Collect all errors defined in templates
foreach my $file (keys %test_templates) {
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
  $file =~ m|template/([^/]+).*/global/([^/]+)-error(?:-errors)?\.html\.tmpl|;
  my $lang    = $1;
  my $errtype = $2;

  if (!open(TMPL, $file)) {
    Register(\%test_templates, $file, "could not open file --WARNING");
    next;
  }

  my $lineno = 0;
  while (my $line = <TMPL>) {
    $lineno++;
    if ($line =~ /\[%\s[A-Z]+\s*error\s*==\s*"(.+)"\s*%\]/) {
      my $errtag = $1;
      if ($errtag =~ /\s/) {
        Register(\%test_templates, $file,
              "has an error definition \"$errtag\" at line $lineno with "
            . "space(s) embedded --ERROR");
      }
      else {
        push @{$Errors{$errtype}{$errtag}{defined_in}{$lang}{$file}}, $lineno;
      }
92
    }
93 94
  }
  close(TMPL);
95 96 97 98
}

# Collect all used errors from cgi/pm files
foreach my $file (keys %test_modules) {
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
  $file =~ s/\s.*$//;    # nuke everything after the first space (#comment)
  next if (!$file);      # skip null entries
  if (!open(TMPL, $file)) {
    Register(\%test_modules, $file, "could not open file --WARNING");
    next;
  }

  my $lineno = 0;
  while (my $line = <TMPL>) {
    last if $line =~ /^__END__/;    # skip the POD (at least in
                                    # Bugzilla/Error.pm)
    $lineno++;
    if ($line
      =~ /^[^#]*\b(Throw(Code|User)Error|(user_)?error\s+=>)\s*\(?\s*["'](.*?)['"]/)
    {
      my $errtype;

      # If it's a normal ThrowCode/UserError
      if ($2) {
        $errtype = lc($2);
      }

      # If it's an AUTH_ERROR tag
      else {
        $errtype = $3 ? 'user' : 'code';
      }
      my $errtag = $4;
      push @{$Errors{$errtype}{$errtag}{used_in}{$file}}, $lineno;
127
    }
128
  }
129

130
  close(TMPL);
131 132 133 134 135
}

# Now let us start the checks

foreach my $errtype (keys %Errors) {
136 137 138 139 140 141 142 143 144 145 146 147
  foreach my $errtag (keys %{$Errors{$errtype}}) {

    # Check for undefined tags
    if (!defined $Errors{$errtype}{$errtag}{defined_in}) {
      UsedIn($errtype, $errtag, "any");
    }
    else {
      # Check for all languages!!!
      my @langs = ();
      foreach my $lang (@languages) {
        if (!defined $Errors{$errtype}{$errtag}{defined_in}{$lang}) {
          push @langs, $lang;
148
        }
149 150 151 152 153 154 155 156 157
      }
      if (scalar @langs) {
        UsedIn($errtype, $errtag, join(', ', @langs));
      }

      # Now check for tag usage in all DEFINED languages
      foreach my $lang (keys %{$Errors{$errtype}{$errtag}{defined_in}}) {
        if (!defined $Errors{$errtype}{$errtag}{used_in}) {
          DefinedIn($errtype, $errtag, $lang);
158
        }
159
      }
160
    }
161
  }
162 163
}

164 165 166
# And make sure that everything defined in WS_ERROR_CODE
# is actually a valid error.
foreach my $err_name (keys %{WS_ERROR_CODE()}) {
167 168 169 170 171 172 173
  if (!defined $Errors{'code'}{$err_name} && !defined $Errors{'user'}{$err_name})
  {
    Register(\%test_modules, 'WS_ERROR_CODE',
          "Error tag '$err_name' is used in WS_ERROR_CODE in"
        . " Bugzilla/WebService/Constants.pm"
        . " but not defined in any template, and not used in any code.");
  }
174 175
}

176 177
# Now report modules results
foreach my $file (sort keys %test_modules) {
178
  Report($file, @{$test_modules{$file}});
179 180
}

181 182 183
# And report WS_ERROR_CODE results
Report('WS_ERROR_CODE', @{$test_modules{'WS_ERROR_CODE'}});

184 185
# Now report templates results
foreach my $file (sort keys %test_templates) {
186
  Report($file, @{$test_templates{$file}});
187 188 189
}

sub Register {
190 191 192 193 194
  my ($hash, $file, $message, $warning) = @_;

  # If set to 1, $warning will avoid the test to fail.
  $warning ||= 0;
  push(@{$hash->{$file}}, {'message' => $message, 'warning' => $warning});
195 196 197
}

sub Report {
198 199 200 201 202 203 204 205 206 207
  my ($file, @errors) = @_;
  if (scalar @errors) {

    # Do we only have warnings to report or also real errors?
    my @real_errors = grep { $_->{'warning'} == 0 } @errors;

    # Extract error messages.
    @errors = map { $_->{'message'} } @errors;
    if (scalar(@real_errors)) {
      ok(0, "$file has " . scalar(@errors) . " error(s):\n" . join("\n", @errors));
208 209
    }
    else {
210 211 212 213 214
      ok(1,
            "--WARNING $file has "
          . scalar(@errors)
          . " unused error tag(s):\n"
          . join("\n", @errors));
215
    }
216 217 218 219 220 221
  }
  else {
    # This is used for both code and template files, so let's use
    # file-independent phrase
    ok(1, "$file uses error tags correctly");
  }
222 223 224
}

sub UsedIn {
225 226 227 228 229 230 231 232
  my ($errtype, $errtag, $lang) = @_;
  $lang = $lang || "any";
  foreach my $file (keys %{$Errors{$errtype}{$errtag}{used_in}}) {
    Register(\%test_modules, $file,
          "$errtype error tag '$errtag' is used at line(s) ("
        . join(',', @{$Errors{$errtype}{$errtag}{used_in}{$file}})
        . ") but not defined for language(s): $lang");
  }
233
}
234

235
sub DefinedIn {
236 237 238 239 240 241 242 243 244 245 246
  my ($errtype, $errtag, $lang) = @_;
  foreach my $file (keys %{$Errors{$errtype}{$errtag}{defined_in}{$lang}}) {
    Register(
      \%test_templates,
      $file,
      "$errtype error tag '$errtag' is defined at line(s) ("
        . join(',', @{$Errors{$errtype}{$errtag}{defined_in}{$lang}{$file}})
        . ") but is not used anywhere",
      1
    );
  }
247 248 249
}

exit 0;