diff options
Diffstat (limited to 'pintos-progos/tests')
451 files changed, 13915 insertions, 0 deletions
diff --git a/pintos-progos/tests/Algorithm/Diff.pm b/pintos-progos/tests/Algorithm/Diff.pm new file mode 100644 index 0000000..904c530 --- /dev/null +++ b/pintos-progos/tests/Algorithm/Diff.pm | |||
| @@ -0,0 +1,1713 @@ | |||
| 1 | package Algorithm::Diff; | ||
| 2 | # Skip to first "=head" line for documentation. | ||
| 3 | use strict; | ||
| 4 | |||
| 5 | use integer; # see below in _replaceNextLargerWith() for mod to make | ||
| 6 | # if you don't use this | ||
| 7 | use vars qw( $VERSION @EXPORT_OK ); | ||
| 8 | $VERSION = 1.19_01; | ||
| 9 | # ^ ^^ ^^-- Incremented at will | ||
| 10 | # | \+----- Incremented for non-trivial changes to features | ||
| 11 | # \-------- Incremented for fundamental changes | ||
| 12 | require Exporter; | ||
| 13 | *import = \&Exporter::import; | ||
| 14 | @EXPORT_OK = qw( | ||
| 15 | prepare LCS LCDidx LCS_length | ||
| 16 | diff sdiff compact_diff | ||
| 17 | traverse_sequences traverse_balanced | ||
| 18 | ); | ||
| 19 | |||
| 20 | # McIlroy-Hunt diff algorithm | ||
| 21 | # Adapted from the Smalltalk code of Mario I. Wolczko, <mario@wolczko.com> | ||
| 22 | # by Ned Konz, perl@bike-nomad.com | ||
| 23 | # Updates by Tye McQueen, http://perlmonks.org/?node=tye | ||
| 24 | |||
| 25 | # Create a hash that maps each element of $aCollection to the set of | ||
| 26 | # positions it occupies in $aCollection, restricted to the elements | ||
| 27 | # within the range of indexes specified by $start and $end. | ||
| 28 | # The fourth parameter is a subroutine reference that will be called to | ||
| 29 | # generate a string to use as a key. | ||
| 30 | # Additional parameters, if any, will be passed to this subroutine. | ||
| 31 | # | ||
| 32 | # my $hashRef = _withPositionsOfInInterval( \@array, $start, $end, $keyGen ); | ||
| 33 | |||
| 34 | sub _withPositionsOfInInterval | ||
| 35 | { | ||
| 36 | my $aCollection = shift; # array ref | ||
| 37 | my $start = shift; | ||
| 38 | my $end = shift; | ||
| 39 | my $keyGen = shift; | ||
| 40 | my %d; | ||
| 41 | my $index; | ||
| 42 | for ( $index = $start ; $index <= $end ; $index++ ) | ||
| 43 | { | ||
| 44 | my $element = $aCollection->[$index]; | ||
| 45 | my $key = &$keyGen( $element, @_ ); | ||
| 46 | if ( exists( $d{$key} ) ) | ||
| 47 | { | ||
| 48 | unshift ( @{ $d{$key} }, $index ); | ||
| 49 | } | ||
| 50 | else | ||
| 51 | { | ||
| 52 | $d{$key} = [$index]; | ||
| 53 | } | ||
| 54 | } | ||
| 55 | return wantarray ? %d : \%d; | ||
| 56 | } | ||
| 57 | |||
| 58 | # Find the place at which aValue would normally be inserted into the | ||
| 59 | # array. If that place is already occupied by aValue, do nothing, and | ||
| 60 | # return undef. If the place does not exist (i.e., it is off the end of | ||
| 61 | # the array), add it to the end, otherwise replace the element at that | ||
| 62 | # point with aValue. It is assumed that the array's values are numeric. | ||
| 63 | # This is where the bulk (75%) of the time is spent in this module, so | ||
| 64 | # try to make it fast! | ||
| 65 | |||
| 66 | sub _replaceNextLargerWith | ||
| 67 | { | ||
| 68 | my ( $array, $aValue, $high ) = @_; | ||
| 69 | $high ||= $#$array; | ||
| 70 | |||
| 71 | # off the end? | ||
| 72 | if ( $high == -1 || $aValue > $array->[-1] ) | ||
| 73 | { | ||
| 74 | push ( @$array, $aValue ); | ||
| 75 | return $high + 1; | ||
| 76 | } | ||
| 77 | |||
| 78 | # binary search for insertion point... | ||
| 79 | my $low = 0; | ||
| 80 | my $index; | ||
| 81 | my $found; | ||
| 82 | while ( $low <= $high ) | ||
| 83 | { | ||
| 84 | $index = ( $high + $low ) / 2; | ||
| 85 | |||
| 86 | # $index = int(( $high + $low ) / 2); # without 'use integer' | ||
| 87 | $found = $array->[$index]; | ||
| 88 | |||
| 89 | if ( $aValue == $found ) | ||
| 90 | { | ||
| 91 | return undef; | ||
| 92 | } | ||
| 93 | elsif ( $aValue > $found ) | ||
| 94 | { | ||
| 95 | $low = $index + 1; | ||
| 96 | } | ||
| 97 | else | ||
| 98 | { | ||
| 99 | $high = $index - 1; | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | # now insertion point is in $low. | ||
| 104 | $array->[$low] = $aValue; # overwrite next larger | ||
| 105 | return $low; | ||
| 106 | } | ||
| 107 | |||
| 108 | # This method computes the longest common subsequence in $a and $b. | ||
| 109 | |||
| 110 | # Result is array or ref, whose contents is such that | ||
| 111 | # $a->[ $i ] == $b->[ $result[ $i ] ] | ||
| 112 | # foreach $i in ( 0 .. $#result ) if $result[ $i ] is defined. | ||
| 113 | |||
| 114 | # An additional argument may be passed; this is a hash or key generating | ||
| 115 | # function that should return a string that uniquely identifies the given | ||
| 116 | # element. It should be the case that if the key is the same, the elements | ||
| 117 | # will compare the same. If this parameter is undef or missing, the key | ||
| 118 | # will be the element as a string. | ||
| 119 | |||
| 120 | # By default, comparisons will use "eq" and elements will be turned into keys | ||
| 121 | # using the default stringizing operator '""'. | ||
| 122 | |||
| 123 | # Additional parameters, if any, will be passed to the key generation | ||
| 124 | # routine. | ||
| 125 | |||
| 126 | sub _longestCommonSubsequence | ||
| 127 | { | ||
| 128 | my $a = shift; # array ref or hash ref | ||
| 129 | my $b = shift; # array ref or hash ref | ||
| 130 | my $counting = shift; # scalar | ||
| 131 | my $keyGen = shift; # code ref | ||
| 132 | my $compare; # code ref | ||
| 133 | |||
| 134 | if ( ref($a) eq 'HASH' ) | ||
| 135 | { # prepared hash must be in $b | ||
| 136 | my $tmp = $b; | ||
| 137 | $b = $a; | ||
| 138 | $a = $tmp; | ||
| 139 | } | ||
| 140 | |||
| 141 | # Check for bogus (non-ref) argument values | ||
| 142 | if ( !ref($a) || !ref($b) ) | ||
| 143 | { | ||
| 144 | my @callerInfo = caller(1); | ||
| 145 | die 'error: must pass array or hash references to ' . $callerInfo[3]; | ||
| 146 | } | ||
| 147 | |||
| 148 | # set up code refs | ||
| 149 | # Note that these are optimized. | ||
| 150 | if ( !defined($keyGen) ) # optimize for strings | ||
| 151 | { | ||
| 152 | $keyGen = sub { $_[0] }; | ||
| 153 | $compare = sub { my ( $a, $b ) = @_; $a eq $b }; | ||
| 154 | } | ||
| 155 | else | ||
| 156 | { | ||
| 157 | $compare = sub { | ||
| 158 | my $a = shift; | ||
| 159 | my $b = shift; | ||
| 160 | &$keyGen( $a, @_ ) eq &$keyGen( $b, @_ ); | ||
| 161 | }; | ||
| 162 | } | ||
| 163 | |||
| 164 | my ( $aStart, $aFinish, $matchVector ) = ( 0, $#$a, [] ); | ||
| 165 | my ( $prunedCount, $bMatches ) = ( 0, {} ); | ||
| 166 | |||
| 167 | if ( ref($b) eq 'HASH' ) # was $bMatches prepared for us? | ||
| 168 | { | ||
| 169 | $bMatches = $b; | ||
| 170 | } | ||
| 171 | else | ||
| 172 | { | ||
| 173 | my ( $bStart, $bFinish ) = ( 0, $#$b ); | ||
| 174 | |||
| 175 | # First we prune off any common elements at the beginning | ||
| 176 | while ( $aStart <= $aFinish | ||
| 177 | and $bStart <= $bFinish | ||
| 178 | and &$compare( $a->[$aStart], $b->[$bStart], @_ ) ) | ||
| 179 | { | ||
| 180 | $matchVector->[ $aStart++ ] = $bStart++; | ||
| 181 | $prunedCount++; | ||
| 182 | } | ||
| 183 | |||
| 184 | # now the end | ||
| 185 | while ( $aStart <= $aFinish | ||
| 186 | and $bStart <= $bFinish | ||
| 187 | and &$compare( $a->[$aFinish], $b->[$bFinish], @_ ) ) | ||
| 188 | { | ||
| 189 | $matchVector->[ $aFinish-- ] = $bFinish--; | ||
| 190 | $prunedCount++; | ||
| 191 | } | ||
| 192 | |||
| 193 | # Now compute the equivalence classes of positions of elements | ||
| 194 | $bMatches = | ||
| 195 | _withPositionsOfInInterval( $b, $bStart, $bFinish, $keyGen, @_ ); | ||
| 196 | } | ||
| 197 | my $thresh = []; | ||
| 198 | my $links = []; | ||
| 199 | |||
| 200 | my ( $i, $ai, $j, $k ); | ||
| 201 | for ( $i = $aStart ; $i <= $aFinish ; $i++ ) | ||
| 202 | { | ||
| 203 | $ai = &$keyGen( $a->[$i], @_ ); | ||
| 204 | if ( exists( $bMatches->{$ai} ) ) | ||
| 205 | { | ||
| 206 | $k = 0; | ||
| 207 | for $j ( @{ $bMatches->{$ai} } ) | ||
| 208 | { | ||
| 209 | |||
| 210 | # optimization: most of the time this will be true | ||
| 211 | if ( $k and $thresh->[$k] > $j and $thresh->[ $k - 1 ] < $j ) | ||
| 212 | { | ||
| 213 | $thresh->[$k] = $j; | ||
| 214 | } | ||
| 215 | else | ||
| 216 | { | ||
| 217 | $k = _replaceNextLargerWith( $thresh, $j, $k ); | ||
| 218 | } | ||
| 219 | |||
| 220 | # oddly, it's faster to always test this (CPU cache?). | ||
| 221 | if ( defined($k) ) | ||
| 222 | { | ||
| 223 | $links->[$k] = | ||
| 224 | [ ( $k ? $links->[ $k - 1 ] : undef ), $i, $j ]; | ||
| 225 | } | ||
| 226 | } | ||
| 227 | } | ||
| 228 | } | ||
| 229 | |||
| 230 | if (@$thresh) | ||
| 231 | { | ||
| 232 | return $prunedCount + @$thresh if $counting; | ||
| 233 | for ( my $link = $links->[$#$thresh] ; $link ; $link = $link->[0] ) | ||
| 234 | { | ||
| 235 | $matchVector->[ $link->[1] ] = $link->[2]; | ||
| 236 | } | ||
| 237 | } | ||
| 238 | elsif ($counting) | ||
| 239 | { | ||
| 240 | return $prunedCount; | ||
| 241 | } | ||
| 242 | |||
| 243 | return wantarray ? @$matchVector : $matchVector; | ||
| 244 | } | ||
| 245 | |||
| 246 | sub traverse_sequences | ||
| 247 | { | ||
| 248 | my $a = shift; # array ref | ||
| 249 | my $b = shift; # array ref | ||
| 250 | my $callbacks = shift || {}; | ||
| 251 | my $keyGen = shift; | ||
| 252 | my $matchCallback = $callbacks->{'MATCH'} || sub { }; | ||
| 253 | my $discardACallback = $callbacks->{'DISCARD_A'} || sub { }; | ||
| 254 | my $finishedACallback = $callbacks->{'A_FINISHED'}; | ||
| 255 | my $discardBCallback = $callbacks->{'DISCARD_B'} || sub { }; | ||
| 256 | my $finishedBCallback = $callbacks->{'B_FINISHED'}; | ||
| 257 | my $matchVector = _longestCommonSubsequence( $a, $b, 0, $keyGen, @_ ); | ||
| 258 | |||
| 259 | # Process all the lines in @$matchVector | ||
| 260 | my $lastA = $#$a; | ||
| 261 | my $lastB = $#$b; | ||
| 262 | my $bi = 0; | ||
| 263 | my $ai; | ||
| 264 | |||
| 265 | for ( $ai = 0 ; $ai <= $#$matchVector ; $ai++ ) | ||
| 266 | { | ||
| 267 | my $bLine = $matchVector->[$ai]; | ||
| 268 | if ( defined($bLine) ) # matched | ||
| 269 | { | ||
| 270 | &$discardBCallback( $ai, $bi++, @_ ) while $bi < $bLine; | ||
| 271 | &$matchCallback( $ai, $bi++, @_ ); | ||
| 272 | } | ||
| 273 | else | ||
| 274 | { | ||
| 275 | &$discardACallback( $ai, $bi, @_ ); | ||
| 276 | } | ||
| 277 | } | ||
| 278 | |||
| 279 | # The last entry (if any) processed was a match. | ||
| 280 | # $ai and $bi point just past the last matching lines in their sequences. | ||
| 281 | |||
| 282 | while ( $ai <= $lastA or $bi <= $lastB ) | ||
| 283 | { | ||
| 284 | |||
| 285 | # last A? | ||
| 286 | if ( $ai == $lastA + 1 and $bi <= $lastB ) | ||
| 287 | { | ||
| 288 | if ( defined($finishedACallback) ) | ||
| 289 | { | ||
| 290 | &$finishedACallback( $lastA, @_ ); | ||
| 291 | $finishedACallback = undef; | ||
| 292 | } | ||
| 293 | else | ||
| 294 | { | ||
| 295 | &$discardBCallback( $ai, $bi++, @_ ) while $bi <= $lastB; | ||
| 296 | } | ||
| 297 | } | ||
| 298 | |||
| 299 | # last B? | ||
| 300 | if ( $bi == $lastB + 1 and $ai <= $lastA ) | ||
| 301 | { | ||
| 302 | if ( defined($finishedBCallback) ) | ||
| 303 | { | ||
| 304 | &$finishedBCallback( $lastB, @_ ); | ||
| 305 | $finishedBCallback = undef; | ||
| 306 | } | ||
| 307 | else | ||
| 308 | { | ||
| 309 | &$discardACallback( $ai++, $bi, @_ ) while $ai <= $lastA; | ||
| 310 | } | ||
| 311 | } | ||
| 312 | |||
| 313 | &$discardACallback( $ai++, $bi, @_ ) if $ai <= $lastA; | ||
| 314 | &$discardBCallback( $ai, $bi++, @_ ) if $bi <= $lastB; | ||
| 315 | } | ||
| 316 | |||
| 317 | return 1; | ||
| 318 | } | ||
| 319 | |||
| 320 | sub traverse_balanced | ||
| 321 | { | ||
| 322 | my $a = shift; # array ref | ||
| 323 | my $b = shift; # array ref | ||
| 324 | my $callbacks = shift || {}; | ||
| 325 | my $keyGen = shift; | ||
| 326 | my $matchCallback = $callbacks->{'MATCH'} || sub { }; | ||
| 327 | my $discardACallback = $callbacks->{'DISCARD_A'} || sub { }; | ||
| 328 | my $discardBCallback = $callbacks->{'DISCARD_B'} || sub { }; | ||
| 329 | my $changeCallback = $callbacks->{'CHANGE'}; | ||
| 330 | my $matchVector = _longestCommonSubsequence( $a, $b, 0, $keyGen, @_ ); | ||
| 331 | |||
| 332 | # Process all the lines in match vector | ||
| 333 | my $lastA = $#$a; | ||
| 334 | my $lastB = $#$b; | ||
| 335 | my $bi = 0; | ||
| 336 | my $ai = 0; | ||
| 337 | my $ma = -1; | ||
| 338 | my $mb; | ||
| 339 | |||
| 340 | while (1) | ||
| 341 | { | ||
| 342 | |||
| 343 | # Find next match indices $ma and $mb | ||
| 344 | do { | ||
| 345 | $ma++; | ||
| 346 | } while( | ||
| 347 | $ma <= $#$matchVector | ||
| 348 | && !defined $matchVector->[$ma] | ||
| 349 | ); | ||
| 350 | |||
| 351 | last if $ma > $#$matchVector; # end of matchVector? | ||
| 352 | $mb = $matchVector->[$ma]; | ||
| 353 | |||
| 354 | # Proceed with discard a/b or change events until | ||
| 355 | # next match | ||
| 356 | while ( $ai < $ma || $bi < $mb ) | ||
| 357 | { | ||
| 358 | |||
| 359 | if ( $ai < $ma && $bi < $mb ) | ||
| 360 | { | ||
| 361 | |||
| 362 | # Change | ||
| 363 | if ( defined $changeCallback ) | ||
| 364 | { | ||
| 365 | &$changeCallback( $ai++, $bi++, @_ ); | ||
| 366 | } | ||
| 367 | else | ||
| 368 | { | ||
| 369 | &$discardACallback( $ai++, $bi, @_ ); | ||
| 370 | &$discardBCallback( $ai, $bi++, @_ ); | ||
| 371 | } | ||
| 372 | } | ||
| 373 | elsif ( $ai < $ma ) | ||
| 374 | { | ||
| 375 | &$discardACallback( $ai++, $bi, @_ ); | ||
| 376 | } | ||
| 377 | else | ||
| 378 | { | ||
| 379 | |||
| 380 | # $bi < $mb | ||
| 381 | &$discardBCallback( $ai, $bi++, @_ ); | ||
| 382 | } | ||
| 383 | } | ||
| 384 | |||
| 385 | # Match | ||
| 386 | &$matchCallback( $ai++, $bi++, @_ ); | ||
| 387 | } | ||
| 388 | |||
| 389 | while ( $ai <= $lastA || $bi <= $lastB ) | ||
| 390 | { | ||
| 391 | if ( $ai <= $lastA && $bi <= $lastB ) | ||
| 392 | { | ||
| 393 | |||
| 394 | # Change | ||
| 395 | if ( defined $changeCallback ) | ||
| 396 | { | ||
| 397 | &$changeCallback( $ai++, $bi++, @_ ); | ||
| 398 | } | ||
| 399 | else | ||
| 400 | { | ||
| 401 | &$discardACallback( $ai++, $bi, @_ ); | ||
| 402 | &$discardBCallback( $ai, $bi++, @_ ); | ||
| 403 | } | ||
| 404 | } | ||
| 405 | elsif ( $ai <= $lastA ) | ||
| 406 | { | ||
| 407 | &$discardACallback( $ai++, $bi, @_ ); | ||
| 408 | } | ||
| 409 | else | ||
| 410 | { | ||
| 411 | |||
| 412 | # $bi <= $lastB | ||
| 413 | &$discardBCallback( $ai, $bi++, @_ ); | ||
| 414 | } | ||
| 415 | } | ||
| 416 | |||
| 417 | return 1; | ||
| 418 | } | ||
| 419 | |||
| 420 | sub prepare | ||
| 421 | { | ||
| 422 | my $a = shift; # array ref | ||
| 423 | my $keyGen = shift; # code ref | ||
| 424 | |||
| 425 | # set up code ref | ||
| 426 | $keyGen = sub { $_[0] } unless defined($keyGen); | ||
| 427 | |||
| 428 | return scalar _withPositionsOfInInterval( $a, 0, $#$a, $keyGen, @_ ); | ||
| 429 | } | ||
| 430 | |||
| 431 | sub LCS | ||
| 432 | { | ||
| 433 | my $a = shift; # array ref | ||
| 434 | my $b = shift; # array ref or hash ref | ||
| 435 | my $matchVector = _longestCommonSubsequence( $a, $b, 0, @_ ); | ||
| 436 | my @retval; | ||
| 437 | my $i; | ||
| 438 | for ( $i = 0 ; $i <= $#$matchVector ; $i++ ) | ||
| 439 | { | ||
| 440 | if ( defined( $matchVector->[$i] ) ) | ||
| 441 | { | ||
| 442 | push ( @retval, $a->[$i] ); | ||
| 443 | } | ||
| 444 | } | ||
| 445 | return wantarray ? @retval : \@retval; | ||
| 446 | } | ||
| 447 | |||
| 448 | sub LCS_length | ||
| 449 | { | ||
| 450 | my $a = shift; # array ref | ||
| 451 | my $b = shift; # array ref or hash ref | ||
| 452 | return _longestCommonSubsequence( $a, $b, 1, @_ ); | ||
| 453 | } | ||
| 454 | |||
| 455 | sub LCSidx | ||
| 456 | { | ||
| 457 | my $a= shift @_; | ||
| 458 | my $b= shift @_; | ||
| 459 | my $match= _longestCommonSubsequence( $a, $b, 0, @_ ); | ||
| 460 | my @am= grep defined $match->[$_], 0..$#$match; | ||
| 461 | my @bm= @{$match}[@am]; | ||
| 462 | return \@am, \@bm; | ||
| 463 | } | ||
| 464 | |||
| 465 | sub compact_diff | ||
| 466 | { | ||
| 467 | my $a= shift @_; | ||
| 468 | my $b= shift @_; | ||
| 469 | my( $am, $bm )= LCSidx( $a, $b, @_ ); | ||
| 470 | my @cdiff; | ||
| 471 | my( $ai, $bi )= ( 0, 0 ); | ||
| 472 | push @cdiff, $ai, $bi; | ||
| 473 | while( 1 ) { | ||
| 474 | while( @$am && $ai == $am->[0] && $bi == $bm->[0] ) { | ||
| 475 | shift @$am; | ||
| 476 | shift @$bm; | ||
| 477 | ++$ai, ++$bi; | ||
| 478 | } | ||
| 479 | push @cdiff, $ai, $bi; | ||
| 480 | last if ! @$am; | ||
| 481 | $ai = $am->[0]; | ||
| 482 | $bi = $bm->[0]; | ||
| 483 | push @cdiff, $ai, $bi; | ||
| 484 | } | ||
| 485 | push @cdiff, 0+@$a, 0+@$b | ||
| 486 | if $ai < @$a || $bi < @$b; | ||
| 487 | return wantarray ? @cdiff : \@cdiff; | ||
| 488 | } | ||
| 489 | |||
| 490 | sub diff | ||
| 491 | { | ||
| 492 | my $a = shift; # array ref | ||
| 493 | my $b = shift; # array ref | ||
| 494 | my $retval = []; | ||
| 495 | my $hunk = []; | ||
| 496 | my $discard = sub { | ||
| 497 | push @$hunk, [ '-', $_[0], $a->[ $_[0] ] ]; | ||
| 498 | }; | ||
| 499 | my $add = sub { | ||
| 500 | push @$hunk, [ '+', $_[1], $b->[ $_[1] ] ]; | ||
| 501 | }; | ||
| 502 | my $match = sub { | ||
| 503 | push @$retval, $hunk | ||
| 504 | if 0 < @$hunk; | ||
| 505 | $hunk = [] | ||
| 506 | }; | ||
| 507 | traverse_sequences( $a, $b, | ||
| 508 | { MATCH => $match, DISCARD_A => $discard, DISCARD_B => $add }, @_ ); | ||
| 509 | &$match(); | ||
| 510 | return wantarray ? @$retval : $retval; | ||
| 511 | } | ||
| 512 | |||
| 513 | sub sdiff | ||
| 514 | { | ||
| 515 | my $a = shift; # array ref | ||
| 516 | my $b = shift; # array ref | ||
| 517 | my $retval = []; | ||
| 518 | my $discard = sub { push ( @$retval, [ '-', $a->[ $_[0] ], "" ] ) }; | ||
| 519 | my $add = sub { push ( @$retval, [ '+', "", $b->[ $_[1] ] ] ) }; | ||
| 520 | my $change = sub { | ||
| 521 | push ( @$retval, [ 'c', $a->[ $_[0] ], $b->[ $_[1] ] ] ); | ||
| 522 | }; | ||
| 523 | my $match = sub { | ||
| 524 | push ( @$retval, [ 'u', $a->[ $_[0] ], $b->[ $_[1] ] ] ); | ||
| 525 | }; | ||
| 526 | traverse_balanced( | ||
| 527 | $a, | ||
| 528 | $b, | ||
| 529 | { | ||
| 530 | MATCH => $match, | ||
| 531 | DISCARD_A => $discard, | ||
| 532 | DISCARD_B => $add, | ||
| 533 | CHANGE => $change, | ||
| 534 | }, | ||
| 535 | @_ | ||
| 536 | ); | ||
| 537 | return wantarray ? @$retval : $retval; | ||
| 538 | } | ||
| 539 | |||
| 540 | ######################################## | ||
| 541 | my $Root= __PACKAGE__; | ||
| 542 | package Algorithm::Diff::_impl; | ||
| 543 | use strict; | ||
| 544 | |||
| 545 | sub _Idx() { 0 } # $me->[_Idx]: Ref to array of hunk indices | ||
| 546 | # 1 # $me->[1]: Ref to first sequence | ||
| 547 | # 2 # $me->[2]: Ref to second sequence | ||
| 548 | sub _End() { 3 } # $me->[_End]: Diff between forward and reverse pos | ||
| 549 | sub _Same() { 4 } # $me->[_Same]: 1 if pos 1 contains unchanged items | ||
| 550 | sub _Base() { 5 } # $me->[_Base]: Added to range's min and max | ||
| 551 | sub _Pos() { 6 } # $me->[_Pos]: Which hunk is currently selected | ||
| 552 | sub _Off() { 7 } # $me->[_Off]: Offset into _Idx for current position | ||
| 553 | sub _Min() { -2 } # Added to _Off to get min instead of max+1 | ||
| 554 | |||
| 555 | sub Die | ||
| 556 | { | ||
| 557 | require Carp; | ||
| 558 | Carp::confess( @_ ); | ||
| 559 | } | ||
| 560 | |||
| 561 | sub _ChkPos | ||
| 562 | { | ||
| 563 | my( $me )= @_; | ||
| 564 | return if $me->[_Pos]; | ||
| 565 | my $meth= ( caller(1) )[3]; | ||
| 566 | Die( "Called $meth on 'reset' object" ); | ||
| 567 | } | ||
| 568 | |||
| 569 | sub _ChkSeq | ||
| 570 | { | ||
| 571 | my( $me, $seq )= @_; | ||
| 572 | return $seq + $me->[_Off] | ||
| 573 | if 1 == $seq || 2 == $seq; | ||
| 574 | my $meth= ( caller(1) )[3]; | ||
| 575 | Die( "$meth: Invalid sequence number ($seq); must be 1 or 2" ); | ||
| 576 | } | ||
| 577 | |||
| 578 | sub getObjPkg | ||
| 579 | { | ||
| 580 | my( $us )= @_; | ||
| 581 | return ref $us if ref $us; | ||
| 582 | return $us . "::_obj"; | ||
| 583 | } | ||
| 584 | |||
| 585 | sub new | ||
| 586 | { | ||
| 587 | my( $us, $seq1, $seq2, $opts ) = @_; | ||
| 588 | my @args; | ||
| 589 | for( $opts->{keyGen} ) { | ||
| 590 | push @args, $_ if $_; | ||
| 591 | } | ||
| 592 | for( $opts->{keyGenArgs} ) { | ||
| 593 | push @args, @$_ if $_; | ||
| 594 | } | ||
| 595 | my $cdif= Algorithm::Diff::compact_diff( $seq1, $seq2, @args ); | ||
| 596 | my $same= 1; | ||
| 597 | if( 0 == $cdif->[2] && 0 == $cdif->[3] ) { | ||
| 598 | $same= 0; | ||
| 599 | splice @$cdif, 0, 2; | ||
| 600 | } | ||
| 601 | my @obj= ( $cdif, $seq1, $seq2 ); | ||
| 602 | $obj[_End] = (1+@$cdif)/2; | ||
| 603 | $obj[_Same] = $same; | ||
| 604 | $obj[_Base] = 0; | ||
| 605 | my $me = bless \@obj, $us->getObjPkg(); | ||
| 606 | $me->Reset( 0 ); | ||
| 607 | return $me; | ||
| 608 | } | ||
| 609 | |||
| 610 | sub Reset | ||
| 611 | { | ||
| 612 | my( $me, $pos )= @_; | ||
| 613 | $pos= int( $pos || 0 ); | ||
| 614 | $pos += $me->[_End] | ||
| 615 | if $pos < 0; | ||
| 616 | $pos= 0 | ||
| 617 | if $pos < 0 || $me->[_End] <= $pos; | ||
| 618 | $me->[_Pos]= $pos || !1; | ||
| 619 | $me->[_Off]= 2*$pos - 1; | ||
| 620 | return $me; | ||
| 621 | } | ||
| 622 | |||
| 623 | sub Base | ||
| 624 | { | ||
| 625 | my( $me, $base )= @_; | ||
| 626 | my $oldBase= $me->[_Base]; | ||
| 627 | $me->[_Base]= 0+$base if defined $base; | ||
| 628 | return $oldBase; | ||
| 629 | } | ||
| 630 | |||
| 631 | sub Copy | ||
| 632 | { | ||
| 633 | my( $me, $pos, $base )= @_; | ||
| 634 | my @obj= @$me; | ||
| 635 | my $you= bless \@obj, ref($me); | ||
| 636 | $you->Reset( $pos ) if defined $pos; | ||
| 637 | $you->Base( $base ); | ||
| 638 | return $you; | ||
| 639 | } | ||
| 640 | |||
| 641 | sub Next { | ||
| 642 | my( $me, $steps )= @_; | ||
| 643 | $steps= 1 if ! defined $steps; | ||
| 644 | if( $steps ) { | ||
| 645 | my $pos= $me->[_Pos]; | ||
| 646 | my $new= $pos + $steps; | ||
| 647 | $new= 0 if $pos && $new < 0; | ||
| 648 | $me->Reset( $new ) | ||
| 649 | } | ||
| 650 | return $me->[_Pos]; | ||
| 651 | } | ||
| 652 | |||
| 653 | sub Prev { | ||
| 654 | my( $me, $steps )= @_; | ||
| 655 | $steps= 1 if ! defined $steps; | ||
| 656 | my $pos= $me->Next(-$steps); | ||
| 657 | $pos -= $me->[_End] if $pos; | ||
| 658 | return $pos; | ||
| 659 | } | ||
| 660 | |||
| 661 | sub Diff { | ||
| 662 | my( $me )= @_; | ||
| 663 | $me->_ChkPos(); | ||
| 664 | return 0 if $me->[_Same] == ( 1 & $me->[_Pos] ); | ||
| 665 | my $ret= 0; | ||
| 666 | my $off= $me->[_Off]; | ||
| 667 | for my $seq ( 1, 2 ) { | ||
| 668 | $ret |= $seq | ||
| 669 | if $me->[_Idx][ $off + $seq + _Min ] | ||
| 670 | < $me->[_Idx][ $off + $seq ]; | ||
| 671 | } | ||
| 672 | return $ret; | ||
| 673 | } | ||
| 674 | |||
| 675 | sub Min { | ||
| 676 | my( $me, $seq, $base )= @_; | ||
| 677 | $me->_ChkPos(); | ||
| 678 | my $off= $me->_ChkSeq($seq); | ||
| 679 | $base= $me->[_Base] if !defined $base; | ||
| 680 | return $base + $me->[_Idx][ $off + _Min ]; | ||
| 681 | } | ||
| 682 | |||
| 683 | sub Max { | ||
| 684 | my( $me, $seq, $base )= @_; | ||
| 685 | $me->_ChkPos(); | ||
| 686 | my $off= $me->_ChkSeq($seq); | ||
| 687 | $base= $me->[_Base] if !defined $base; | ||
| 688 | return $base + $me->[_Idx][ $off ] -1; | ||
| 689 | } | ||
| 690 | |||
| 691 | sub Range { | ||
| 692 | my( $me, $seq, $base )= @_; | ||
| 693 | $me->_ChkPos(); | ||
| 694 | my $off = $me->_ChkSeq($seq); | ||
| 695 | if( !wantarray ) { | ||
| 696 | return $me->[_Idx][ $off ] | ||
| 697 | - $me->[_Idx][ $off + _Min ]; | ||
| 698 | } | ||
| 699 | $base= $me->[_Base] if !defined $base; | ||
| 700 | return ( $base + $me->[_Idx][ $off + _Min ] ) | ||
| 701 | .. ( $base + $me->[_Idx][ $off ] - 1 ); | ||
| 702 | } | ||
| 703 | |||
| 704 | sub Items { | ||
| 705 | my( $me, $seq )= @_; | ||
| 706 | $me->_ChkPos(); | ||
| 707 | my $off = $me->_ChkSeq($seq); | ||
| 708 | if( !wantarray ) { | ||
| 709 | return $me->[_Idx][ $off ] | ||
| 710 | - $me->[_Idx][ $off + _Min ]; | ||
| 711 | } | ||
| 712 | return | ||
| 713 | @{$me->[$seq]}[ | ||
| 714 | $me->[_Idx][ $off + _Min ] | ||
| 715 | .. ( $me->[_Idx][ $off ] - 1 ) | ||
| 716 | ]; | ||
| 717 | } | ||
| 718 | |||
| 719 | sub Same { | ||
| 720 | my( $me )= @_; | ||
| 721 | $me->_ChkPos(); | ||
| 722 | return wantarray ? () : 0 | ||
| 723 | if $me->[_Same] != ( 1 & $me->[_Pos] ); | ||
| 724 | return $me->Items(1); | ||
| 725 | } | ||
| 726 | |||
| 727 | my %getName; | ||
| 728 | BEGIN { | ||
| 729 | %getName= ( | ||
| 730 | same => \&Same, | ||
| 731 | diff => \&Diff, | ||
| 732 | base => \&Base, | ||
| 733 | min => \&Min, | ||
| 734 | max => \&Max, | ||
| 735 | range=> \&Range, | ||
| 736 | items=> \&Items, # same thing | ||
| 737 | ); | ||
| 738 | } | ||
| 739 | |||
| 740 | sub Get | ||
| 741 | { | ||
| 742 | my $me= shift @_; | ||
| 743 | $me->_ChkPos(); | ||
| 744 | my @value; | ||
| 745 | for my $arg ( @_ ) { | ||
| 746 | for my $word ( split ' ', $arg ) { | ||
| 747 | my $meth; | ||
| 748 | if( $word !~ /^(-?\d+)?([a-zA-Z]+)([12])?$/ | ||
| 749 | || not $meth= $getName{ lc $2 } | ||
| 750 | ) { | ||
| 751 | Die( $Root, ", Get: Invalid request ($word)" ); | ||
| 752 | } | ||
| 753 | my( $base, $name, $seq )= ( $1, $2, $3 ); | ||
| 754 | push @value, scalar( | ||
| 755 | 4 == length($name) | ||
| 756 | ? $meth->( $me ) | ||
| 757 | : $meth->( $me, $seq, $base ) | ||
| 758 | ); | ||
| 759 | } | ||
| 760 | } | ||
| 761 | if( wantarray ) { | ||
| 762 | return @value; | ||
| 763 | } elsif( 1 == @value ) { | ||
| 764 | return $value[0]; | ||
| 765 | } | ||
| 766 | Die( 0+@value, " values requested from ", | ||
| 767 | $Root, "'s Get in scalar context" ); | ||
| 768 | } | ||
| 769 | |||
| 770 | |||
| 771 | my $Obj= getObjPkg($Root); | ||
| 772 | no strict 'refs'; | ||
| 773 | |||
| 774 | for my $meth ( qw( new getObjPkg ) ) { | ||
| 775 | *{$Root."::".$meth} = \&{$meth}; | ||
| 776 | *{$Obj ."::".$meth} = \&{$meth}; | ||
| 777 | } | ||
| 778 | for my $meth ( qw( | ||
| 779 | Next Prev Reset Copy Base Diff | ||
| 780 | Same Items Range Min Max Get | ||
| 781 | _ChkPos _ChkSeq | ||
| 782 | ) ) { | ||
| 783 | *{$Obj."::".$meth} = \&{$meth}; | ||
| 784 | } | ||
| 785 | |||
| 786 | 1; | ||
| 787 | __END__ | ||
| 788 | |||
| 789 | =head1 NAME | ||
| 790 | |||
| 791 | Algorithm::Diff - Compute `intelligent' differences between two files / lists | ||
| 792 | |||
| 793 | =head1 SYNOPSIS | ||
| 794 | |||
| 795 | require Algorithm::Diff; | ||
| 796 | |||
| 797 | # This example produces traditional 'diff' output: | ||
| 798 | |||
| 799 | my $diff = Algorithm::Diff->new( \@seq1, \@seq2 ); | ||
| 800 | |||
| 801 | $diff->Base( 1 ); # Return line numbers, not indices | ||
| 802 | while( $diff->Next() ) { | ||
| 803 | next if $diff->Same(); | ||
| 804 | my $sep = ''; | ||
| 805 | if( ! $diff->Items(2) ) { | ||
| 806 | sprintf "%d,%dd%d\n", | ||
| 807 | $diff->Get(qw( Min1 Max1 Max2 )); | ||
| 808 | } elsif( ! $diff->Items(1) ) { | ||
| 809 | sprint "%da%d,%d\n", | ||
| 810 | $diff->Get(qw( Max1 Min2 Max2 )); | ||
| 811 | } else { | ||
| 812 | $sep = "---\n"; | ||
| 813 | sprintf "%d,%dc%d,%d\n", | ||
| 814 | $diff->Get(qw( Min1 Max1 Min2 Max2 )); | ||
| 815 | } | ||
| 816 | print "< $_" for $diff->Items(1); | ||
| 817 | print $sep; | ||
| 818 | print "> $_" for $diff->Items(2); | ||
| 819 | } | ||
| 820 | |||
| 821 | |||
| 822 | # Alternate interfaces: | ||
| 823 | |||
| 824 | use Algorithm::Diff qw( | ||
| 825 | LCS LCS_length LCSidx | ||
| 826 | diff sdiff compact_diff | ||
| 827 | traverse_sequences traverse_balanced ); | ||
| 828 | |||
| 829 | @lcs = LCS( \@seq1, \@seq2 ); | ||
| 830 | $lcsref = LCS( \@seq1, \@seq2 ); | ||
| 831 | $count = LCS_length( \@seq1, \@seq2 ); | ||
| 832 | |||
| 833 | ( $seq1idxref, $seq2idxref ) = LCSidx( \@seq1, \@seq2 ); | ||
| 834 | |||
| 835 | |||
| 836 | # Complicated interfaces: | ||
| 837 | |||
| 838 | @diffs = diff( \@seq1, \@seq2 ); | ||
| 839 | |||
| 840 | @sdiffs = sdiff( \@seq1, \@seq2 ); | ||
| 841 | |||
| 842 | @cdiffs = compact_diff( \@seq1, \@seq2 ); | ||
| 843 | |||
| 844 | traverse_sequences( | ||
| 845 | \@seq1, | ||
| 846 | \@seq2, | ||
| 847 | { MATCH => \&callback1, | ||
| 848 | DISCARD_A => \&callback2, | ||
| 849 | DISCARD_B => \&callback3, | ||
| 850 | }, | ||
| 851 | \&key_generator, | ||
| 852 | @extra_args, | ||
| 853 | ); | ||
| 854 | |||
| 855 | traverse_balanced( | ||
| 856 | \@seq1, | ||
| 857 | \@seq2, | ||
| 858 | { MATCH => \&callback1, | ||
| 859 | DISCARD_A => \&callback2, | ||
| 860 | DISCARD_B => \&callback3, | ||
| 861 | CHANGE => \&callback4, | ||
| 862 | }, | ||
| 863 | \&key_generator, | ||
| 864 | @extra_args, | ||
| 865 | ); | ||
| 866 | |||
| 867 | |||
| 868 | =head1 INTRODUCTION | ||
| 869 | |||
| 870 | (by Mark-Jason Dominus) | ||
| 871 | |||
| 872 | I once read an article written by the authors of C<diff>; they said | ||
| 873 | that they worked very hard on the algorithm until they found the | ||
| 874 | right one. | ||
| 875 | |||
| 876 | I think what they ended up using (and I hope someone will correct me, | ||
| 877 | because I am not very confident about this) was the `longest common | ||
| 878 | subsequence' method. In the LCS problem, you have two sequences of | ||
| 879 | items: | ||
| 880 | |||
| 881 | a b c d f g h j q z | ||
| 882 | |||
| 883 | a b c d e f g i j k r x y z | ||
| 884 | |||
| 885 | and you want to find the longest sequence of items that is present in | ||
| 886 | both original sequences in the same order. That is, you want to find | ||
| 887 | a new sequence I<S> which can be obtained from the first sequence by | ||
| 888 | deleting some items, and from the secend sequence by deleting other | ||
| 889 | items. You also want I<S> to be as long as possible. In this case I<S> | ||
| 890 | is | ||
| 891 | |||
| 892 | a b c d f g j z | ||
| 893 | |||
| 894 | From there it's only a small step to get diff-like output: | ||
| 895 | |||
| 896 | e h i k q r x y | ||
| 897 | + - + + - + + + | ||
| 898 | |||
| 899 | This module solves the LCS problem. It also includes a canned function | ||
| 900 | to generate C<diff>-like output. | ||
| 901 | |||
| 902 | It might seem from the example above that the LCS of two sequences is | ||
| 903 | always pretty obvious, but that's not always the case, especially when | ||
| 904 | the two sequences have many repeated elements. For example, consider | ||
| 905 | |||
| 906 | a x b y c z p d q | ||
| 907 | a b c a x b y c z | ||
| 908 | |||
| 909 | A naive approach might start by matching up the C<a> and C<b> that | ||
| 910 | appear at the beginning of each sequence, like this: | ||
| 911 | |||
| 912 | a x b y c z p d q | ||
| 913 | a b c a b y c z | ||
| 914 | |||
| 915 | This finds the common subsequence C<a b c z>. But actually, the LCS | ||
| 916 | is C<a x b y c z>: | ||
| 917 | |||
| 918 | a x b y c z p d q | ||
| 919 | a b c a x b y c z | ||
| 920 | |||
| 921 | or | ||
| 922 | |||
| 923 | a x b y c z p d q | ||
| 924 | a b c a x b y c z | ||
| 925 | |||
| 926 | =head1 USAGE | ||
| 927 | |||
| 928 | (See also the README file and several example | ||
| 929 | scripts include with this module.) | ||
| 930 | |||
| 931 | This module now provides an object-oriented interface that uses less | ||
| 932 | memory and is easier to use than most of the previous procedural | ||
| 933 | interfaces. It also still provides several exportable functions. We'll | ||
| 934 | deal with these in ascending order of difficulty: C<LCS>, | ||
| 935 | C<LCS_length>, C<LCSidx>, OO interface, C<prepare>, C<diff>, C<sdiff>, | ||
| 936 | C<traverse_sequences>, and C<traverse_balanced>. | ||
| 937 | |||
| 938 | =head2 C<LCS> | ||
| 939 | |||
| 940 | Given references to two lists of items, LCS returns an array containing | ||
| 941 | their longest common subsequence. In scalar context, it returns a | ||
| 942 | reference to such a list. | ||
| 943 | |||
| 944 | @lcs = LCS( \@seq1, \@seq2 ); | ||
| 945 | $lcsref = LCS( \@seq1, \@seq2 ); | ||
| 946 | |||
| 947 | C<LCS> may be passed an optional third parameter; this is a CODE | ||
| 948 | reference to a key generation function. See L</KEY GENERATION | ||
| 949 | FUNCTIONS>. | ||
| 950 | |||
| 951 | @lcs = LCS( \@seq1, \@seq2, \&keyGen, @args ); | ||
| 952 | $lcsref = LCS( \@seq1, \@seq2, \&keyGen, @args ); | ||
| 953 | |||
| 954 | Additional parameters, if any, will be passed to the key generation | ||
| 955 | routine. | ||
| 956 | |||
| 957 | =head2 C<LCS_length> | ||
| 958 | |||
| 959 | This is just like C<LCS> except it only returns the length of the | ||
| 960 | longest common subsequence. This provides a performance gain of about | ||
| 961 | 9% compared to C<LCS>. | ||
| 962 | |||
| 963 | =head2 C<LCSidx> | ||
| 964 | |||
| 965 | Like C<LCS> except it returns references to two arrays. The first array | ||
| 966 | contains the indices into @seq1 where the LCS items are located. The | ||
| 967 | second array contains the indices into @seq2 where the LCS items are located. | ||
| 968 | |||
| 969 | Therefore, the following three lists will contain the same values: | ||
| 970 | |||
| 971 | my( $idx1, $idx2 ) = LCSidx( \@seq1, \@seq2 ); | ||
| 972 | my @list1 = @seq1[ @$idx1 ]; | ||
| 973 | my @list2 = @seq2[ @$idx2 ]; | ||
| 974 | my @list3 = LCS( \@seq1, \@seq2 ); | ||
| 975 | |||
| 976 | =head2 C<new> | ||
| 977 | |||
| 978 | $diff = Algorithm::Diffs->new( \@seq1, \@seq2 ); | ||
| 979 | $diff = Algorithm::Diffs->new( \@seq1, \@seq2, \%opts ); | ||
| 980 | |||
| 981 | C<new> computes the smallest set of additions and deletions necessary | ||
| 982 | to turn the first sequence into the second and compactly records them | ||
| 983 | in the object. | ||
| 984 | |||
| 985 | You use the object to iterate over I<hunks>, where each hunk represents | ||
| 986 | a contiguous section of items which should be added, deleted, replaced, | ||
| 987 | or left unchanged. | ||
| 988 | |||
| 989 | =over 4 | ||
| 990 | |||
| 991 | The following summary of all of the methods looks a lot like Perl code | ||
| 992 | but some of the symbols have different meanings: | ||
| 993 | |||
| 994 | [ ] Encloses optional arguments | ||
| 995 | : Is followed by the default value for an optional argument | ||
| 996 | | Separates alternate return results | ||
| 997 | |||
| 998 | Method summary: | ||
| 999 | |||
| 1000 | $obj = Algorithm::Diff->new( \@seq1, \@seq2, [ \%opts ] ); | ||
| 1001 | $pos = $obj->Next( [ $count : 1 ] ); | ||
| 1002 | $revPos = $obj->Prev( [ $count : 1 ] ); | ||
| 1003 | $obj = $obj->Reset( [ $pos : 0 ] ); | ||
| 1004 | $copy = $obj->Copy( [ $pos, [ $newBase ] ] ); | ||
| 1005 | $oldBase = $obj->Base( [ $newBase ] ); | ||
| 1006 | |||
| 1007 | Note that all of the following methods C<die> if used on an object that | ||
| 1008 | is "reset" (not currently pointing at any hunk). | ||
| 1009 | |||
| 1010 | $bits = $obj->Diff( ); | ||
| 1011 | @items|$cnt = $obj->Same( ); | ||
| 1012 | @items|$cnt = $obj->Items( $seqNum ); | ||
| 1013 | @idxs |$cnt = $obj->Range( $seqNum, [ $base ] ); | ||
| 1014 | $minIdx = $obj->Min( $seqNum, [ $base ] ); | ||
| 1015 | $maxIdx = $obj->Max( $seqNum, [ $base ] ); | ||
| 1016 | @values = $obj->Get( @names ); | ||
| 1017 | |||
| 1018 | Passing in C<undef> for an optional argument is always treated the same | ||
| 1019 | as if no argument were passed in. | ||
| 1020 | |||
| 1021 | =item C<Next> | ||
| 1022 | |||
| 1023 | $pos = $diff->Next(); # Move forward 1 hunk | ||
| 1024 | $pos = $diff->Next( 2 ); # Move forward 2 hunks | ||
| 1025 | $pos = $diff->Next(-5); # Move backward 5 hunks | ||
| 1026 | |||
| 1027 | C<Next> moves the object to point at the next hunk. The object starts | ||
| 1028 | out "reset", which means it isn't pointing at any hunk. If the object | ||
| 1029 | is reset, then C<Next()> moves to the first hunk. | ||
| 1030 | |||
| 1031 | C<Next> returns a true value iff the move didn't go past the last hunk. | ||
| 1032 | So C<Next(0)> will return true iff the object is not reset. | ||
| 1033 | |||
| 1034 | Actually, C<Next> returns the object's new position, which is a number | ||
| 1035 | between 1 and the number of hunks (inclusive), or returns a false value. | ||
| 1036 | |||
| 1037 | =item C<Prev> | ||
| 1038 | |||
| 1039 | C<Prev($N)> is almost identical to C<Next(-$N)>; it moves to the $Nth | ||
| 1040 | previous hunk. On a 'reset' object, C<Prev()> [and C<Next(-1)>] move | ||
| 1041 | to the last hunk. | ||
| 1042 | |||
| 1043 | The position returned by C<Prev> is relative to the I<end> of the | ||
| 1044 | hunks; -1 for the last hunk, -2 for the second-to-last, etc. | ||
| 1045 | |||
| 1046 | =item C<Reset> | ||
| 1047 | |||
| 1048 | $diff->Reset(); # Reset the object's position | ||
| 1049 | $diff->Reset($pos); # Move to the specified hunk | ||
| 1050 | $diff->Reset(1); # Move to the first hunk | ||
| 1051 | $diff->Reset(-1); # Move to the last hunk | ||
| 1052 | |||
| 1053 | C<Reset> returns the object, so, for example, you could use | ||
| 1054 | C<< $diff->Reset()->Next(-1) >> to get the number of hunks. | ||
| 1055 | |||
| 1056 | =item C<Copy> | ||
| 1057 | |||
| 1058 | $copy = $diff->Copy( $newPos, $newBase ); | ||
| 1059 | |||
| 1060 | C<Copy> returns a copy of the object. The copy and the orignal object | ||
| 1061 | share most of their data, so making copies takes very little memory. | ||
| 1062 | The copy maintains its own position (separate from the original), which | ||
| 1063 | is the main purpose of copies. It also maintains its own base. | ||
| 1064 | |||
| 1065 | By default, the copy's position starts out the same as the original | ||
| 1066 | object's position. But C<Copy> takes an optional first argument to set the | ||
| 1067 | new position, so the following three snippets are equivalent: | ||
| 1068 | |||
| 1069 | $copy = $diff->Copy($pos); | ||
| 1070 | |||
| 1071 | $copy = $diff->Copy(); | ||
| 1072 | $copy->Reset($pos); | ||
| 1073 | |||
| 1074 | $copy = $diff->Copy()->Reset($pos); | ||
| 1075 | |||
| 1076 | C<Copy> takes an optional second argument to set the base for | ||
| 1077 | the copy. If you wish to change the base of the copy but leave | ||
| 1078 | the position the same as in the original, here are two | ||
| 1079 | equivalent ways: | ||
| 1080 | |||
| 1081 | $copy = $diff->Copy(); | ||
| 1082 | $copy->Base( 0 ); | ||
| 1083 | |||
| 1084 | $copy = $diff->Copy(undef,0); | ||
| 1085 | |||
| 1086 | Here are two equivalent way to get a "reset" copy: | ||
| 1087 | |||
| 1088 | $copy = $diff->Copy(0); | ||
| 1089 | |||
| 1090 | $copy = $diff->Copy()->Reset(); | ||
| 1091 | |||
| 1092 | =item C<Diff> | ||
| 1093 | |||
| 1094 | $bits = $obj->Diff(); | ||
| 1095 | |||
| 1096 | C<Diff> returns a true value iff the current hunk contains items that are | ||
| 1097 | different between the two sequences. It actually returns one of the | ||
| 1098 | follow 4 values: | ||
| 1099 | |||
| 1100 | =over 4 | ||
| 1101 | |||
| 1102 | =item 3 | ||
| 1103 | |||
| 1104 | C<3==(1|2)>. This hunk contains items from @seq1 and the items | ||
| 1105 | from @seq2 that should replace them. Both sequence 1 and 2 | ||
| 1106 | contain changed items so both the 1 and 2 bits are set. | ||
| 1107 | |||
| 1108 | =item 2 | ||
| 1109 | |||
| 1110 | This hunk only contains items from @seq2 that should be inserted (not | ||
| 1111 | items from @seq1). Only sequence 2 contains changed items so only the 2 | ||
| 1112 | bit is set. | ||
| 1113 | |||
| 1114 | =item 1 | ||
| 1115 | |||
| 1116 | This hunk only contains items from @seq1 that should be deleted (not | ||
| 1117 | items from @seq2). Only sequence 1 contains changed items so only the 1 | ||
| 1118 | bit is set. | ||
| 1119 | |||
| 1120 | =item 0 | ||
| 1121 | |||
| 1122 | This means that the items in this hunk are the same in both sequences. | ||
| 1123 | Neither sequence 1 nor 2 contain changed items so neither the 1 nor the | ||
| 1124 | 2 bits are set. | ||
| 1125 | |||
| 1126 | =back | ||
| 1127 | |||
| 1128 | =item C<Same> | ||
| 1129 | |||
| 1130 | C<Same> returns a true value iff the current hunk contains items that | ||
| 1131 | are the same in both sequences. It actually returns the list of items | ||
| 1132 | if they are the same or an emty list if they aren't. In a scalar | ||
| 1133 | context, it returns the size of the list. | ||
| 1134 | |||
| 1135 | =item C<Items> | ||
| 1136 | |||
| 1137 | $count = $diff->Items(2); | ||
| 1138 | @items = $diff->Items($seqNum); | ||
| 1139 | |||
| 1140 | C<Items> returns the (number of) items from the specified sequence that | ||
| 1141 | are part of the current hunk. | ||
| 1142 | |||
| 1143 | If the current hunk contains only insertions, then | ||
| 1144 | C<< $diff->Items(1) >> will return an empty list (0 in a scalar conext). | ||
| 1145 | If the current hunk contains only deletions, then C<< $diff->Items(2) >> | ||
| 1146 | will return an empty list (0 in a scalar conext). | ||
| 1147 | |||
| 1148 | If the hunk contains replacements, then both C<< $diff->Items(1) >> and | ||
| 1149 | C<< $diff->Items(2) >> will return different, non-empty lists. | ||
| 1150 | |||
| 1151 | Otherwise, the hunk contains identical items and all of the following | ||
| 1152 | will return the same lists: | ||
| 1153 | |||
| 1154 | @items = $diff->Items(1); | ||
| 1155 | @items = $diff->Items(2); | ||
| 1156 | @items = $diff->Same(); | ||
| 1157 | |||
| 1158 | =item C<Range> | ||
| 1159 | |||
| 1160 | $count = $diff->Range( $seqNum ); | ||
| 1161 | @indices = $diff->Range( $seqNum ); | ||
| 1162 | @indices = $diff->Range( $seqNum, $base ); | ||
| 1163 | |||
| 1164 | C<Range> is like C<Items> except that it returns a list of I<indices> to | ||
| 1165 | the items rather than the items themselves. By default, the index of | ||
| 1166 | the first item (in each sequence) is 0 but this can be changed by | ||
| 1167 | calling the C<Base> method. So, by default, the following two snippets | ||
| 1168 | return the same lists: | ||
| 1169 | |||
| 1170 | @list = $diff->Items(2); | ||
| 1171 | @list = @seq2[ $diff->Range(2) ]; | ||
| 1172 | |||
| 1173 | You can also specify the base to use as the second argument. So the | ||
| 1174 | following two snippets I<always> return the same lists: | ||
| 1175 | |||
| 1176 | @list = $diff->Items(1); | ||
| 1177 | @list = @seq1[ $diff->Range(1,0) ]; | ||
| 1178 | |||
| 1179 | =item C<Base> | ||
| 1180 | |||
| 1181 | $curBase = $diff->Base(); | ||
| 1182 | $oldBase = $diff->Base($newBase); | ||
| 1183 | |||
| 1184 | C<Base> sets and/or returns the current base (usually 0 or 1) that is | ||
| 1185 | used when you request range information. The base defaults to 0 so | ||
| 1186 | that range information is returned as array indices. You can set the | ||
| 1187 | base to 1 if you want to report traditional line numbers instead. | ||
| 1188 | |||
| 1189 | =item C<Min> | ||
| 1190 | |||
| 1191 | $min1 = $diff->Min(1); | ||
| 1192 | $min = $diff->Min( $seqNum, $base ); | ||
| 1193 | |||
| 1194 | C<Min> returns the first value that C<Range> would return (given the | ||
| 1195 | same arguments) or returns C<undef> if C<Range> would return an empty | ||
| 1196 | list. | ||
| 1197 | |||
| 1198 | =item C<Max> | ||
| 1199 | |||
| 1200 | C<Max> returns the last value that C<Range> would return or C<undef>. | ||
| 1201 | |||
| 1202 | =item C<Get> | ||
| 1203 | |||
| 1204 | ( $n, $x, $r ) = $diff->Get(qw( min1 max1 range1 )); | ||
| 1205 | @values = $diff->Get(qw( 0min2 1max2 range2 same base )); | ||
| 1206 | |||
| 1207 | C<Get> returns one or more scalar values. You pass in a list of the | ||
| 1208 | names of the values you want returned. Each name must match one of the | ||
| 1209 | following regexes: | ||
| 1210 | |||
| 1211 | /^(-?\d+)?(min|max)[12]$/i | ||
| 1212 | /^(range[12]|same|diff|base)$/i | ||
| 1213 | |||
| 1214 | The 1 or 2 after a name says which sequence you want the information | ||
| 1215 | for (and where allowed, it is required). The optional number before | ||
| 1216 | "min" or "max" is the base to use. So the following equalities hold: | ||
| 1217 | |||
| 1218 | $diff->Get('min1') == $diff->Min(1) | ||
| 1219 | $diff->Get('0min2') == $diff->Min(2,0) | ||
| 1220 | |||
| 1221 | Using C<Get> in a scalar context when you've passed in more than one | ||
| 1222 | name is a fatal error (C<die> is called). | ||
| 1223 | |||
| 1224 | =back | ||
| 1225 | |||
| 1226 | =head2 C<prepare> | ||
| 1227 | |||
| 1228 | Given a reference to a list of items, C<prepare> returns a reference | ||
| 1229 | to a hash which can be used when comparing this sequence to other | ||
| 1230 | sequences with C<LCS> or C<LCS_length>. | ||
| 1231 | |||
| 1232 | $prep = prepare( \@seq1 ); | ||
| 1233 | for $i ( 0 .. 10_000 ) | ||
| 1234 | { | ||
| 1235 | @lcs = LCS( $prep, $seq[$i] ); | ||
| 1236 | # do something useful with @lcs | ||
| 1237 | } | ||
| 1238 | |||
| 1239 | C<prepare> may be passed an optional third parameter; this is a CODE | ||
| 1240 | reference to a key generation function. See L</KEY GENERATION | ||
| 1241 | FUNCTIONS>. | ||
| 1242 | |||
| 1243 | $prep = prepare( \@seq1, \&keyGen ); | ||
| 1244 | for $i ( 0 .. 10_000 ) | ||
| 1245 | { | ||
| 1246 | @lcs = LCS( $seq[$i], $prep, \&keyGen ); | ||
| 1247 | # do something useful with @lcs | ||
| 1248 | } | ||
| 1249 | |||
| 1250 | Using C<prepare> provides a performance gain of about 50% when calling LCS | ||
| 1251 | many times compared with not preparing. | ||
| 1252 | |||
| 1253 | =head2 C<diff> | ||
| 1254 | |||
| 1255 | @diffs = diff( \@seq1, \@seq2 ); | ||
| 1256 | $diffs_ref = diff( \@seq1, \@seq2 ); | ||
| 1257 | |||
| 1258 | C<diff> computes the smallest set of additions and deletions necessary | ||
| 1259 | to turn the first sequence into the second, and returns a description | ||
| 1260 | of these changes. The description is a list of I<hunks>; each hunk | ||
| 1261 | represents a contiguous section of items which should be added, | ||
| 1262 | deleted, or replaced. (Hunks containing unchanged items are not | ||
| 1263 | included.) | ||
| 1264 | |||
| 1265 | The return value of C<diff> is a list of hunks, or, in scalar context, a | ||
| 1266 | reference to such a list. If there are no differences, the list will be | ||
| 1267 | empty. | ||
| 1268 | |||
| 1269 | Here is an example. Calling C<diff> for the following two sequences: | ||
| 1270 | |||
| 1271 | a b c e h j l m n p | ||
| 1272 | b c d e f j k l m r s t | ||
| 1273 | |||
| 1274 | would produce the following list: | ||
| 1275 | |||
| 1276 | ( | ||
| 1277 | [ [ '-', 0, 'a' ] ], | ||
| 1278 | |||
| 1279 | [ [ '+', 2, 'd' ] ], | ||
| 1280 | |||
| 1281 | [ [ '-', 4, 'h' ], | ||
| 1282 | [ '+', 4, 'f' ] ], | ||
| 1283 | |||
| 1284 | [ [ '+', 6, 'k' ] ], | ||
| 1285 | |||
| 1286 | [ [ '-', 8, 'n' ], | ||
| 1287 | [ '-', 9, 'p' ], | ||
| 1288 | [ '+', 9, 'r' ], | ||
| 1289 | [ '+', 10, 's' ], | ||
| 1290 | [ '+', 11, 't' ] ], | ||
| 1291 | ) | ||
| 1292 | |||
| 1293 | There are five hunks here. The first hunk says that the C<a> at | ||
| 1294 | position 0 of the first sequence should be deleted (C<->). The second | ||
| 1295 | hunk says that the C<d> at position 2 of the second sequence should | ||
| 1296 | be inserted (C<+>). The third hunk says that the C<h> at position 4 | ||
| 1297 | of the first sequence should be removed and replaced with the C<f> | ||
| 1298 | from position 4 of the second sequence. And so on. | ||
| 1299 | |||
| 1300 | C<diff> may be passed an optional third parameter; this is a CODE | ||
| 1301 | reference to a key generation function. See L</KEY GENERATION | ||
| 1302 | FUNCTIONS>. | ||
| 1303 | |||
| 1304 | Additional parameters, if any, will be passed to the key generation | ||
| 1305 | routine. | ||
| 1306 | |||
| 1307 | =head2 C<sdiff> | ||
| 1308 | |||
| 1309 | @sdiffs = sdiff( \@seq1, \@seq2 ); | ||
| 1310 | $sdiffs_ref = sdiff( \@seq1, \@seq2 ); | ||
| 1311 | |||
| 1312 | C<sdiff> computes all necessary components to show two sequences | ||
| 1313 | and their minimized differences side by side, just like the | ||
| 1314 | Unix-utility I<sdiff> does: | ||
| 1315 | |||
| 1316 | same same | ||
| 1317 | before | after | ||
| 1318 | old < - | ||
| 1319 | - > new | ||
| 1320 | |||
| 1321 | It returns a list of array refs, each pointing to an array of | ||
| 1322 | display instructions. In scalar context it returns a reference | ||
| 1323 | to such a list. If there are no differences, the list will have one | ||
| 1324 | entry per item, each indicating that the item was unchanged. | ||
| 1325 | |||
| 1326 | Display instructions consist of three elements: A modifier indicator | ||
| 1327 | (C<+>: Element added, C<->: Element removed, C<u>: Element unmodified, | ||
| 1328 | C<c>: Element changed) and the value of the old and new elements, to | ||
| 1329 | be displayed side-by-side. | ||
| 1330 | |||
| 1331 | An C<sdiff> of the following two sequences: | ||
| 1332 | |||
| 1333 | a b c e h j l m n p | ||
| 1334 | b c d e f j k l m r s t | ||
| 1335 | |||
| 1336 | results in | ||
| 1337 | |||
| 1338 | ( [ '-', 'a', '' ], | ||
| 1339 | [ 'u', 'b', 'b' ], | ||
| 1340 | [ 'u', 'c', 'c' ], | ||
| 1341 | [ '+', '', 'd' ], | ||
| 1342 | [ 'u', 'e', 'e' ], | ||
| 1343 | [ 'c', 'h', 'f' ], | ||
| 1344 | [ 'u', 'j', 'j' ], | ||
| 1345 | [ '+', '', 'k' ], | ||
| 1346 | [ 'u', 'l', 'l' ], | ||
| 1347 | [ 'u', 'm', 'm' ], | ||
| 1348 | [ 'c', 'n', 'r' ], | ||
| 1349 | [ 'c', 'p', 's' ], | ||
| 1350 | [ '+', '', 't' ], | ||
| 1351 | ) | ||
| 1352 | |||
| 1353 | C<sdiff> may be passed an optional third parameter; this is a CODE | ||
| 1354 | reference to a key generation function. See L</KEY GENERATION | ||
| 1355 | FUNCTIONS>. | ||
| 1356 | |||
| 1357 | Additional parameters, if any, will be passed to the key generation | ||
| 1358 | routine. | ||
| 1359 | |||
| 1360 | =head2 C<compact_diff> | ||
| 1361 | |||
| 1362 | C<compact_diff> is much like C<sdiff> except it returns a much more | ||
| 1363 | compact description consisting of just one flat list of indices. An | ||
| 1364 | example helps explain the format: | ||
| 1365 | |||
| 1366 | my @a = qw( a b c e h j l m n p ); | ||
| 1367 | my @b = qw( b c d e f j k l m r s t ); | ||
| 1368 | @cdiff = compact_diff( \@a, \@b ); | ||
| 1369 | # Returns: | ||
| 1370 | # @a @b @a @b | ||
| 1371 | # start start values values | ||
| 1372 | ( 0, 0, # = | ||
| 1373 | 0, 0, # a ! | ||
| 1374 | 1, 0, # b c = b c | ||
| 1375 | 3, 2, # ! d | ||
| 1376 | 3, 3, # e = e | ||
| 1377 | 4, 4, # f ! h | ||
| 1378 | 5, 5, # j = j | ||
| 1379 | 6, 6, # ! k | ||
| 1380 | 6, 7, # l m = l m | ||
| 1381 | 8, 9, # n p ! r s t | ||
| 1382 | 10, 12, # | ||
| 1383 | ); | ||
| 1384 | |||
| 1385 | The 0th, 2nd, 4th, etc. entries are all indices into @seq1 (@a in the | ||
| 1386 | above example) indicating where a hunk begins. The 1st, 3rd, 5th, etc. | ||
| 1387 | entries are all indices into @seq2 (@b in the above example) indicating | ||
| 1388 | where the same hunk begins. | ||
| 1389 | |||
| 1390 | So each pair of indices (except the last pair) describes where a hunk | ||
| 1391 | begins (in each sequence). Since each hunk must end at the item just | ||
| 1392 | before the item that starts the next hunk, the next pair of indices can | ||
| 1393 | be used to determine where the hunk ends. | ||
| 1394 | |||
| 1395 | So, the first 4 entries (0..3) describe the first hunk. Entries 0 and 1 | ||
| 1396 | describe where the first hunk begins (and so are always both 0). | ||
| 1397 | Entries 2 and 3 describe where the next hunk begins, so subtracting 1 | ||
| 1398 | from each tells us where the first hunk ends. That is, the first hunk | ||
| 1399 | contains items C<$diff[0]> through C<$diff[2] - 1> of the first sequence | ||
| 1400 | and contains items C<$diff[1]> through C<$diff[3] - 1> of the second | ||
| 1401 | sequence. | ||
| 1402 | |||
| 1403 | In other words, the first hunk consists of the following two lists of items: | ||
| 1404 | |||
| 1405 | # 1st pair 2nd pair | ||
| 1406 | # of indices of indices | ||
| 1407 | @list1 = @a[ $cdiff[0] .. $cdiff[2]-1 ]; | ||
| 1408 | @list2 = @b[ $cdiff[1] .. $cdiff[3]-1 ]; | ||
| 1409 | # Hunk start Hunk end | ||
| 1410 | |||
| 1411 | Note that the hunks will always alternate between those that are part of | ||
| 1412 | the LCS (those that contain unchanged items) and those that contain | ||
| 1413 | changes. This means that all we need to be told is whether the first | ||
| 1414 | hunk is a 'same' or 'diff' hunk and we can determine which of the other | ||
| 1415 | hunks contain 'same' items or 'diff' items. | ||
| 1416 | |||
| 1417 | By convention, we always make the first hunk contain unchanged items. | ||
| 1418 | So the 1st, 3rd, 5th, etc. hunks (all odd-numbered hunks if you start | ||
| 1419 | counting from 1) all contain unchanged items. And the 2nd, 4th, 6th, | ||
| 1420 | etc. hunks (all even-numbered hunks if you start counting from 1) all | ||
| 1421 | contain changed items. | ||
| 1422 | |||
| 1423 | Since @a and @b don't begin with the same value, the first hunk in our | ||
| 1424 | example is empty (otherwise we'd violate the above convention). Note | ||
| 1425 | that the first 4 index values in our example are all zero. Plug these | ||
| 1426 | values into our previous code block and we get: | ||
| 1427 | |||
| 1428 | @hunk1a = @a[ 0 .. 0-1 ]; | ||
| 1429 | @hunk1b = @b[ 0 .. 0-1 ]; | ||
| 1430 | |||
| 1431 | And C<0..-1> returns the empty list. | ||
| 1432 | |||
| 1433 | Move down one pair of indices (2..5) and we get the offset ranges for | ||
| 1434 | the second hunk, which contains changed items. | ||
| 1435 | |||
| 1436 | Since C<@diff[2..5]> contains (0,0,1,0) in our example, the second hunk | ||
| 1437 | consists of these two lists of items: | ||
| 1438 | |||
| 1439 | @hunk2a = @a[ $cdiff[2] .. $cdiff[4]-1 ]; | ||
| 1440 | @hunk2b = @b[ $cdiff[3] .. $cdiff[5]-1 ]; | ||
| 1441 | # or | ||
| 1442 | @hunk2a = @a[ 0 .. 1-1 ]; | ||
| 1443 | @hunk2b = @b[ 0 .. 0-1 ]; | ||
| 1444 | # or | ||
| 1445 | @hunk2a = @a[ 0 .. 0 ]; | ||
| 1446 | @hunk2b = @b[ 0 .. -1 ]; | ||
| 1447 | # or | ||
| 1448 | @hunk2a = ( 'a' ); | ||
| 1449 | @hunk2b = ( ); | ||
| 1450 | |||
| 1451 | That is, we would delete item 0 ('a') from @a. | ||
| 1452 | |||
| 1453 | Since C<@diff[4..7]> contains (1,0,3,2) in our example, the third hunk | ||
| 1454 | consists of these two lists of items: | ||
| 1455 | |||
| 1456 | @hunk3a = @a[ $cdiff[4] .. $cdiff[6]-1 ]; | ||
| 1457 | @hunk3a = @b[ $cdiff[5] .. $cdiff[7]-1 ]; | ||
| 1458 | # or | ||
| 1459 | @hunk3a = @a[ 1 .. 3-1 ]; | ||
| 1460 | @hunk3a = @b[ 0 .. 2-1 ]; | ||
| 1461 | # or | ||
| 1462 | @hunk3a = @a[ 1 .. 2 ]; | ||
| 1463 | @hunk3a = @b[ 0 .. 1 ]; | ||
| 1464 | # or | ||
| 1465 | @hunk3a = qw( b c ); | ||
| 1466 | @hunk3a = qw( b c ); | ||
| 1467 | |||
| 1468 | Note that this third hunk contains unchanged items as our convention demands. | ||
| 1469 | |||
| 1470 | You can continue this process until you reach the last two indices, | ||
| 1471 | which will always be the number of items in each sequence. This is | ||
| 1472 | required so that subtracting one from each will give you the indices to | ||
| 1473 | the last items in each sequence. | ||
| 1474 | |||
| 1475 | =head2 C<traverse_sequences> | ||
| 1476 | |||
| 1477 | C<traverse_sequences> used to be the most general facility provided by | ||
| 1478 | this module (the new OO interface is more powerful and much easier to | ||
| 1479 | use). | ||
| 1480 | |||
| 1481 | Imagine that there are two arrows. Arrow A points to an element of | ||
| 1482 | sequence A, and arrow B points to an element of the sequence B. | ||
| 1483 | Initially, the arrows point to the first elements of the respective | ||
| 1484 | sequences. C<traverse_sequences> will advance the arrows through the | ||
| 1485 | sequences one element at a time, calling an appropriate user-specified | ||
| 1486 | callback function before each advance. It willadvance the arrows in | ||
| 1487 | such a way that if there are equal elements C<$A[$i]> and C<$B[$j]> | ||
| 1488 | which are equal and which are part of the LCS, there will be some moment | ||
| 1489 | during the execution of C<traverse_sequences> when arrow A is pointing | ||
| 1490 | to C<$A[$i]> and arrow B is pointing to C<$B[$j]>. When this happens, | ||
| 1491 | C<traverse_sequences> will call the C<MATCH> callback function and then | ||
| 1492 | it will advance both arrows. | ||
| 1493 | |||
| 1494 | Otherwise, one of the arrows is pointing to an element of its sequence | ||
| 1495 | that is not part of the LCS. C<traverse_sequences> will advance that | ||
| 1496 | arrow and will call the C<DISCARD_A> or the C<DISCARD_B> callback, | ||
| 1497 | depending on which arrow it advanced. If both arrows point to elements | ||
| 1498 | that are not part of the LCS, then C<traverse_sequences> will advance | ||
| 1499 | one of them and call the appropriate callback, but it is not specified | ||
| 1500 | which it will call. | ||
| 1501 | |||
| 1502 | The arguments to C<traverse_sequences> are the two sequences to | ||
| 1503 | traverse, and a hash which specifies the callback functions, like this: | ||
| 1504 | |||
| 1505 | traverse_sequences( | ||
| 1506 | \@seq1, \@seq2, | ||
| 1507 | { MATCH => $callback_1, | ||
| 1508 | DISCARD_A => $callback_2, | ||
| 1509 | DISCARD_B => $callback_3, | ||
| 1510 | } | ||
| 1511 | ); | ||
| 1512 | |||
| 1513 | Callbacks for MATCH, DISCARD_A, and DISCARD_B are invoked with at least | ||
| 1514 | the indices of the two arrows as their arguments. They are not expected | ||
| 1515 | to return any values. If a callback is omitted from the table, it is | ||
| 1516 | not called. | ||
| 1517 | |||
| 1518 | Callbacks for A_FINISHED and B_FINISHED are invoked with at least the | ||
| 1519 | corresponding index in A or B. | ||
| 1520 | |||
| 1521 | If arrow A reaches the end of its sequence, before arrow B does, | ||
| 1522 | C<traverse_sequences> will call the C<A_FINISHED> callback when it | ||
| 1523 | advances arrow B, if there is such a function; if not it will call | ||
| 1524 | C<DISCARD_B> instead. Similarly if arrow B finishes first. | ||
| 1525 | C<traverse_sequences> returns when both arrows are at the ends of their | ||
| 1526 | respective sequences. It returns true on success and false on failure. | ||
| 1527 | At present there is no way to fail. | ||
| 1528 | |||
| 1529 | C<traverse_sequences> may be passed an optional fourth parameter; this | ||
| 1530 | is a CODE reference to a key generation function. See L</KEY GENERATION | ||
| 1531 | FUNCTIONS>. | ||
| 1532 | |||
| 1533 | Additional parameters, if any, will be passed to the key generation function. | ||
| 1534 | |||
| 1535 | If you want to pass additional parameters to your callbacks, but don't | ||
| 1536 | need a custom key generation function, you can get the default by | ||
| 1537 | passing undef: | ||
| 1538 | |||
| 1539 | traverse_sequences( | ||
| 1540 | \@seq1, \@seq2, | ||
| 1541 | { MATCH => $callback_1, | ||
| 1542 | DISCARD_A => $callback_2, | ||
| 1543 | DISCARD_B => $callback_3, | ||
| 1544 | }, | ||
| 1545 | undef, # default key-gen | ||
| 1546 | $myArgument1, | ||
| 1547 | $myArgument2, | ||
| 1548 | $myArgument3, | ||
| 1549 | ); | ||
| 1550 | |||
| 1551 | C<traverse_sequences> does not have a useful return value; you are | ||
| 1552 | expected to plug in the appropriate behavior with the callback | ||
| 1553 | functions. | ||
| 1554 | |||
| 1555 | =head2 C<traverse_balanced> | ||
| 1556 | |||
| 1557 | C<traverse_balanced> is an alternative to C<traverse_sequences>. It | ||
| 1558 | uses a different algorithm to iterate through the entries in the | ||
| 1559 | computed LCS. Instead of sticking to one side and showing element changes | ||
| 1560 | as insertions and deletions only, it will jump back and forth between | ||
| 1561 | the two sequences and report I<changes> occurring as deletions on one | ||
| 1562 | side followed immediatly by an insertion on the other side. | ||
| 1563 | |||
| 1564 | In addition to the C<DISCARD_A>, C<DISCARD_B>, and C<MATCH> callbacks | ||
| 1565 | supported by C<traverse_sequences>, C<traverse_balanced> supports | ||
| 1566 | a C<CHANGE> callback indicating that one element got C<replaced> by another: | ||
| 1567 | |||
| 1568 | traverse_balanced( | ||
| 1569 | \@seq1, \@seq2, | ||
| 1570 | { MATCH => $callback_1, | ||
| 1571 | DISCARD_A => $callback_2, | ||
| 1572 | DISCARD_B => $callback_3, | ||
| 1573 | CHANGE => $callback_4, | ||
| 1574 | } | ||
| 1575 | ); | ||
| 1576 | |||
| 1577 | If no C<CHANGE> callback is specified, C<traverse_balanced> | ||
| 1578 | will map C<CHANGE> events to C<DISCARD_A> and C<DISCARD_B> actions, | ||
| 1579 | therefore resulting in a similar behaviour as C<traverse_sequences> | ||
| 1580 | with different order of events. | ||
| 1581 | |||
| 1582 | C<traverse_balanced> might be a bit slower than C<traverse_sequences>, | ||
| 1583 | noticable only while processing huge amounts of data. | ||
| 1584 | |||
| 1585 | The C<sdiff> function of this module | ||
| 1586 | is implemented as call to C<traverse_balanced>. | ||
| 1587 | |||
| 1588 | C<traverse_balanced> does not have a useful return value; you are expected to | ||
| 1589 | plug in the appropriate behavior with the callback functions. | ||
| 1590 | |||
| 1591 | =head1 KEY GENERATION FUNCTIONS | ||
| 1592 | |||
| 1593 | Most of the functions accept an optional extra parameter. This is a | ||
| 1594 | CODE reference to a key generating (hashing) function that should return | ||
| 1595 | a string that uniquely identifies a given element. It should be the | ||
| 1596 | case that if two elements are to be considered equal, their keys should | ||
| 1597 | be the same (and the other way around). If no key generation function | ||
| 1598 | is provided, the key will be the element as a string. | ||
| 1599 | |||
| 1600 | By default, comparisons will use "eq" and elements will be turned into keys | ||
| 1601 | using the default stringizing operator '""'. | ||
| 1602 | |||
| 1603 | Where this is important is when you're comparing something other than | ||
| 1604 | strings. If it is the case that you have multiple different objects | ||
| 1605 | that should be considered to be equal, you should supply a key | ||
| 1606 | generation function. Otherwise, you have to make sure that your arrays | ||
| 1607 | contain unique references. | ||
| 1608 | |||
| 1609 | For instance, consider this example: | ||
| 1610 | |||
| 1611 | package Person; | ||
| 1612 | |||
| 1613 | sub new | ||
| 1614 | { | ||
| 1615 | my $package = shift; | ||
| 1616 | return bless { name => '', ssn => '', @_ }, $package; | ||
| 1617 | } | ||
| 1618 | |||
| 1619 | sub clone | ||
| 1620 | { | ||
| 1621 | my $old = shift; | ||
| 1622 | my $new = bless { %$old }, ref($old); | ||
| 1623 | } | ||
| 1624 | |||
| 1625 | sub hash | ||
| 1626 | { | ||
| 1627 | return shift()->{'ssn'}; | ||
| 1628 | } | ||
| 1629 | |||
| 1630 | my $person1 = Person->new( name => 'Joe', ssn => '123-45-6789' ); | ||
| 1631 | my $person2 = Person->new( name => 'Mary', ssn => '123-47-0000' ); | ||
| 1632 | my $person3 = Person->new( name => 'Pete', ssn => '999-45-2222' ); | ||
| 1633 | my $person4 = Person->new( name => 'Peggy', ssn => '123-45-9999' ); | ||
| 1634 | my $person5 = Person->new( name => 'Frank', ssn => '000-45-9999' ); | ||
| 1635 | |||
| 1636 | If you did this: | ||
| 1637 | |||
| 1638 | my $array1 = [ $person1, $person2, $person4 ]; | ||
| 1639 | my $array2 = [ $person1, $person3, $person4, $person5 ]; | ||
| 1640 | Algorithm::Diff::diff( $array1, $array2 ); | ||
| 1641 | |||
| 1642 | everything would work out OK (each of the objects would be converted | ||
| 1643 | into a string like "Person=HASH(0x82425b0)" for comparison). | ||
| 1644 | |||
| 1645 | But if you did this: | ||
| 1646 | |||
| 1647 | my $array1 = [ $person1, $person2, $person4 ]; | ||
| 1648 | my $array2 = [ $person1, $person3, $person4->clone(), $person5 ]; | ||
| 1649 | Algorithm::Diff::diff( $array1, $array2 ); | ||
| 1650 | |||
| 1651 | $person4 and $person4->clone() (which have the same name and SSN) | ||
| 1652 | would be seen as different objects. If you wanted them to be considered | ||
| 1653 | equivalent, you would have to pass in a key generation function: | ||
| 1654 | |||
| 1655 | my $array1 = [ $person1, $person2, $person4 ]; | ||
| 1656 | my $array2 = [ $person1, $person3, $person4->clone(), $person5 ]; | ||
| 1657 | Algorithm::Diff::diff( $array1, $array2, \&Person::hash ); | ||
| 1658 | |||
| 1659 | This would use the 'ssn' field in each Person as a comparison key, and | ||
| 1660 | so would consider $person4 and $person4->clone() as equal. | ||
| 1661 | |||
| 1662 | You may also pass additional parameters to the key generation function | ||
| 1663 | if you wish. | ||
| 1664 | |||
| 1665 | =head1 ERROR CHECKING | ||
| 1666 | |||
| 1667 | If you pass these routines a non-reference and they expect a reference, | ||
| 1668 | they will die with a message. | ||
| 1669 | |||
| 1670 | =head1 AUTHOR | ||
| 1671 | |||
| 1672 | This version released by Tye McQueen (http://perlmonks.org/?node=tye). | ||
| 1673 | |||
| 1674 | =head1 LICENSE | ||
| 1675 | |||
| 1676 | Parts Copyright (c) 2000-2004 Ned Konz. All rights reserved. | ||
| 1677 | Parts by Tye McQueen. | ||
| 1678 | |||
| 1679 | This program is free software; you can redistribute it and/or modify it | ||
| 1680 | under the same terms as Perl. | ||
| 1681 | |||
| 1682 | =head1 MAILING LIST | ||
| 1683 | |||
| 1684 | Mark-Jason still maintains a mailing list. To join a low-volume mailing | ||
| 1685 | list for announcements related to diff and Algorithm::Diff, send an | ||
| 1686 | empty mail message to mjd-perl-diff-request@plover.com. | ||
| 1687 | |||
| 1688 | =head1 CREDITS | ||
| 1689 | |||
| 1690 | Versions through 0.59 (and much of this documentation) were written by: | ||
| 1691 | |||
| 1692 | Mark-Jason Dominus, mjd-perl-diff@plover.com | ||
| 1693 | |||
| 1694 | This version borrows some documentation and routine names from | ||
| 1695 | Mark-Jason's, but Diff.pm's code was completely replaced. | ||
| 1696 | |||
| 1697 | This code was adapted from the Smalltalk code of Mario Wolczko | ||
| 1698 | <mario@wolczko.com>, which is available at | ||
| 1699 | ftp://st.cs.uiuc.edu/pub/Smalltalk/MANCHESTER/manchester/4.0/diff.st | ||
| 1700 | |||
| 1701 | C<sdiff> and C<traverse_balanced> were written by Mike Schilli | ||
| 1702 | <m@perlmeister.com>. | ||
| 1703 | |||
| 1704 | The algorithm is that described in | ||
| 1705 | I<A Fast Algorithm for Computing Longest Common Subsequences>, | ||
| 1706 | CACM, vol.20, no.5, pp.350-353, May 1977, with a few | ||
| 1707 | minor improvements to improve the speed. | ||
| 1708 | |||
| 1709 | Much work was done by Ned Konz (perl@bike-nomad.com). | ||
| 1710 | |||
| 1711 | The OO interface and some other changes are by Tye McQueen. | ||
| 1712 | |||
| 1713 | =cut | ||
diff --git a/pintos-progos/tests/Make.tests b/pintos-progos/tests/Make.tests new file mode 100644 index 0000000..358e697 --- /dev/null +++ b/pintos-progos/tests/Make.tests | |||
| @@ -0,0 +1,76 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | |||
| 3 | include $(patsubst %,$(SRCDIR)/%/Make.tests,$(TEST_SUBDIRS)) | ||
| 4 | |||
| 5 | PROGS = $(foreach subdir,$(TEST_SUBDIRS),$($(subdir)_PROGS)) | ||
| 6 | TESTS = $(foreach subdir,$(TEST_SUBDIRS),$($(subdir)_TESTS)) | ||
| 7 | EXTRA_GRADES = $(foreach subdir,$(TEST_SUBDIRS),$($(subdir)_EXTRA_GRADES)) | ||
| 8 | |||
| 9 | OUTPUTS = $(addsuffix .output,$(TESTS) $(EXTRA_GRADES)) | ||
| 10 | ERRORS = $(addsuffix .errors,$(TESTS) $(EXTRA_GRADES)) | ||
| 11 | RESULTS = $(addsuffix .result,$(TESTS) $(EXTRA_GRADES)) | ||
| 12 | |||
| 13 | ifdef PROGS | ||
| 14 | include ../../Makefile.userprog | ||
| 15 | endif | ||
| 16 | |||
| 17 | TIMEOUT = 60 | ||
| 18 | |||
| 19 | clean:: | ||
| 20 | rm -f $(OUTPUTS) $(ERRORS) $(RESULTS) | ||
| 21 | |||
| 22 | grade:: results | ||
| 23 | $(SRCDIR)/tests/make-grade $(SRCDIR) $< $(GRADING_FILE) | tee $@ | ||
| 24 | |||
| 25 | check:: results | ||
| 26 | @cat $< | ||
| 27 | @COUNT="`egrep '^(pass|FAIL) ' $< | wc -l | sed 's/[ ]//g;'`"; \ | ||
| 28 | FAILURES="`egrep '^FAIL ' $< | wc -l | sed 's/[ ]//g;'`"; \ | ||
| 29 | if [ $$FAILURES = 0 ]; then \ | ||
| 30 | echo "All $$COUNT tests passed."; \ | ||
| 31 | else \ | ||
| 32 | echo "$$FAILURES of $$COUNT tests failed."; \ | ||
| 33 | exit 1; \ | ||
| 34 | fi | ||
| 35 | |||
| 36 | results: $(RESULTS) | ||
| 37 | @for d in $(TESTS) $(EXTRA_GRADES); do \ | ||
| 38 | if echo PASS | cmp -s $$d.result -; then \ | ||
| 39 | echo "pass $$d"; \ | ||
| 40 | else \ | ||
| 41 | echo "FAIL $$d"; \ | ||
| 42 | fi; \ | ||
| 43 | done > $@ | ||
| 44 | |||
| 45 | outputs:: $(OUTPUTS) | ||
| 46 | |||
| 47 | $(foreach prog,$(PROGS),$(eval $(prog).output: $(prog))) | ||
| 48 | $(foreach test,$(TESTS),$(eval $(test).output: $($(test)_PUTFILES))) | ||
| 49 | $(foreach test,$(TESTS),$(eval $(test).output: TEST = $(test))) | ||
| 50 | |||
| 51 | # Prevent an environment variable VERBOSE from surprising us. | ||
| 52 | VERBOSE = | ||
| 53 | |||
| 54 | TESTCMD = pintos -v -k -T $(TIMEOUT) | ||
| 55 | TESTCMD += $(SIMULATOR) | ||
| 56 | TESTCMD += $(PINTOSOPTS) | ||
| 57 | ifeq ($(filter userprog, $(KERNEL_SUBDIRS)), userprog) | ||
| 58 | TESTCMD += $(FILESYSSOURCE) | ||
| 59 | TESTCMD += $(foreach file,$(PUTFILES),-p $(file) -a $(notdir $(file))) | ||
| 60 | endif | ||
| 61 | ifeq ($(filter vm, $(KERNEL_SUBDIRS)), vm) | ||
| 62 | TESTCMD += --swap-size=4 | ||
| 63 | endif | ||
| 64 | TESTCMD += -- -q | ||
| 65 | TESTCMD += $(KERNELFLAGS) | ||
| 66 | ifeq ($(filter userprog, $(KERNEL_SUBDIRS)), userprog) | ||
| 67 | TESTCMD += -f | ||
| 68 | endif | ||
| 69 | TESTCMD += $(if $($(TEST)_ARGS),run '$(*F) $($(TEST)_ARGS)',run $(*F)) | ||
| 70 | TESTCMD += < /dev/null | ||
| 71 | TESTCMD += 2> $(TEST).errors $(if $(VERBOSE),|tee,>) $(TEST).output | ||
| 72 | %.output: kernel.bin loader.bin | ||
| 73 | $(TESTCMD) | ||
| 74 | |||
| 75 | %.result: %.ck %.output | ||
| 76 | perl -I$(SRCDIR) $< $* $@ | ||
diff --git a/pintos-progos/tests/arc4.c b/pintos-progos/tests/arc4.c new file mode 100644 index 0000000..b033cc6 --- /dev/null +++ b/pintos-progos/tests/arc4.c | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | #include <stdint.h> | ||
| 2 | #include "tests/arc4.h" | ||
| 3 | |||
| 4 | /* Swap bytes. */ | ||
| 5 | static inline void | ||
| 6 | swap_byte (uint8_t *a, uint8_t *b) | ||
| 7 | { | ||
| 8 | uint8_t t = *a; | ||
| 9 | *a = *b; | ||
| 10 | *b = t; | ||
| 11 | } | ||
| 12 | |||
| 13 | void | ||
| 14 | arc4_init (struct arc4 *arc4, const void *key_, size_t size) | ||
| 15 | { | ||
| 16 | const uint8_t *key = key_; | ||
| 17 | size_t key_idx; | ||
| 18 | uint8_t *s; | ||
| 19 | int i, j; | ||
| 20 | |||
| 21 | s = arc4->s; | ||
| 22 | arc4->i = arc4->j = 0; | ||
| 23 | for (i = 0; i < 256; i++) | ||
| 24 | s[i] = i; | ||
| 25 | for (key_idx = 0, i = j = 0; i < 256; i++) | ||
| 26 | { | ||
| 27 | j = (j + s[i] + key[key_idx]) & 255; | ||
| 28 | swap_byte (s + i, s + j); | ||
| 29 | if (++key_idx >= size) | ||
| 30 | key_idx = 0; | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | void | ||
| 35 | arc4_crypt (struct arc4 *arc4, void *buf_, size_t size) | ||
| 36 | { | ||
| 37 | uint8_t *buf = buf_; | ||
| 38 | uint8_t *s; | ||
| 39 | uint8_t i, j; | ||
| 40 | |||
| 41 | s = arc4->s; | ||
| 42 | i = arc4->i; | ||
| 43 | j = arc4->j; | ||
| 44 | while (size-- > 0) | ||
| 45 | { | ||
| 46 | i += 1; | ||
| 47 | j += s[i]; | ||
| 48 | swap_byte (s + i, s + j); | ||
| 49 | *buf++ ^= s[(s[i] + s[j]) & 255]; | ||
| 50 | } | ||
| 51 | arc4->i = i; | ||
| 52 | arc4->j = j; | ||
| 53 | } | ||
diff --git a/pintos-progos/tests/arc4.h b/pintos-progos/tests/arc4.h new file mode 100644 index 0000000..61c533a --- /dev/null +++ b/pintos-progos/tests/arc4.h | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | #ifndef TESTS_ARC4_H | ||
| 2 | #define TESTS_ARC4_H | ||
| 3 | |||
| 4 | #include <stddef.h> | ||
| 5 | #include <stdint.h> | ||
| 6 | |||
| 7 | /* Alleged RC4 algorithm encryption state. */ | ||
| 8 | struct arc4 | ||
| 9 | { | ||
| 10 | uint8_t s[256]; | ||
| 11 | uint8_t i, j; | ||
| 12 | }; | ||
| 13 | |||
| 14 | void arc4_init (struct arc4 *, const void *, size_t); | ||
| 15 | void arc4_crypt (struct arc4 *, void *, size_t); | ||
| 16 | |||
| 17 | #endif /* tests/arc4.h */ | ||
diff --git a/pintos-progos/tests/arc4.pm b/pintos-progos/tests/arc4.pm new file mode 100644 index 0000000..df19216 --- /dev/null +++ b/pintos-progos/tests/arc4.pm | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | use strict; | ||
| 2 | use warnings; | ||
| 3 | |||
| 4 | sub arc4_init { | ||
| 5 | my ($key) = @_; | ||
| 6 | my (@s) = 0...255; | ||
| 7 | my ($j) = 0; | ||
| 8 | for my $i (0...255) { | ||
| 9 | $j = ($j + $s[$i] + ord (substr ($key, $i % length ($key), 1))) & 0xff; | ||
| 10 | @s[$i, $j] = @s[$j, $i]; | ||
| 11 | } | ||
| 12 | return (0, 0, @s); | ||
| 13 | } | ||
| 14 | |||
| 15 | sub arc4_crypt { | ||
| 16 | my ($arc4, $buf) = @_; | ||
| 17 | my ($i, $j, @s) = @$arc4; | ||
| 18 | my ($out) = ""; | ||
| 19 | for my $c (split (//, $buf)) { | ||
| 20 | $i = ($i + 1) & 0xff; | ||
| 21 | $j = ($j + $s[$i]) & 0xff; | ||
| 22 | @s[$i, $j] = @s[$j, $i]; | ||
| 23 | $out .= chr (ord ($c) ^ $s[($s[$i] + $s[$j]) & 0xff]); | ||
| 24 | } | ||
| 25 | @$arc4 = ($i, $j, @s); | ||
| 26 | return $out; | ||
| 27 | } | ||
| 28 | |||
| 29 | 1; | ||
diff --git a/pintos-progos/tests/cksum.c b/pintos-progos/tests/cksum.c new file mode 100644 index 0000000..92a2995 --- /dev/null +++ b/pintos-progos/tests/cksum.c | |||
| @@ -0,0 +1,92 @@ | |||
| 1 | /* crctab[] and cksum() are from the `cksum' entry in SUSv3. */ | ||
| 2 | |||
| 3 | #include <stdint.h> | ||
| 4 | #include "tests/cksum.h" | ||
| 5 | |||
| 6 | static unsigned long crctab[] = { | ||
| 7 | 0x00000000, | ||
| 8 | 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, | ||
| 9 | 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, | ||
| 10 | 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, | ||
| 11 | 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, | ||
| 12 | 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, | ||
| 13 | 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, | ||
| 14 | 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, | ||
| 15 | 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, | ||
| 16 | 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, | ||
| 17 | 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, | ||
| 18 | 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, | ||
| 19 | 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, | ||
| 20 | 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, | ||
| 21 | 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, | ||
| 22 | 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, | ||
| 23 | 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, | ||
| 24 | 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, | ||
| 25 | 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, | ||
| 26 | 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, | ||
| 27 | 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, | ||
| 28 | 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, | ||
| 29 | 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, | ||
| 30 | 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, | ||
| 31 | 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f, | ||
| 32 | 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, | ||
| 33 | 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, | ||
| 34 | 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, | ||
| 35 | 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, | ||
| 36 | 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, | ||
| 37 | 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, | ||
| 38 | 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, | ||
| 39 | 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, | ||
| 40 | 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, | ||
| 41 | 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, | ||
| 42 | 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, | ||
| 43 | 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, | ||
| 44 | 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, | ||
| 45 | 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, | ||
| 46 | 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, | ||
| 47 | 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, | ||
| 48 | 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, | ||
| 49 | 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, | ||
| 50 | 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, | ||
| 51 | 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, | ||
| 52 | 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, | ||
| 53 | 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, | ||
| 54 | 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, | ||
| 55 | 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09, | ||
| 56 | 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, | ||
| 57 | 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, | ||
| 58 | 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 | ||
| 59 | }; | ||
| 60 | |||
| 61 | /* This is the algorithm used by the Posix `cksum' utility. */ | ||
| 62 | unsigned long | ||
| 63 | cksum (const void *b_, size_t n) | ||
| 64 | { | ||
| 65 | const unsigned char *b = b_; | ||
| 66 | uint32_t s = 0; | ||
| 67 | size_t i; | ||
| 68 | for (i = n; i > 0; --i) | ||
| 69 | { | ||
| 70 | unsigned char c = *b++; | ||
| 71 | s = (s << 8) ^ crctab[(s >> 24) ^ c]; | ||
| 72 | } | ||
| 73 | while (n != 0) | ||
| 74 | { | ||
| 75 | unsigned char c = n; | ||
| 76 | n >>= 8; | ||
| 77 | s = (s << 8) ^ crctab[(s >> 24) ^ c]; | ||
| 78 | } | ||
| 79 | return ~s; | ||
| 80 | } | ||
| 81 | |||
| 82 | #ifdef STANDALONE_TEST | ||
| 83 | #include <stdio.h> | ||
| 84 | int | ||
| 85 | main (void) | ||
| 86 | { | ||
| 87 | char buf[65536]; | ||
| 88 | int n = fread (buf, 1, sizeof buf, stdin); | ||
| 89 | printf ("%lu\n", cksum (buf, n)); | ||
| 90 | return 0; | ||
| 91 | } | ||
| 92 | #endif | ||
diff --git a/pintos-progos/tests/cksum.h b/pintos-progos/tests/cksum.h new file mode 100644 index 0000000..23a1fe9 --- /dev/null +++ b/pintos-progos/tests/cksum.h | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | #ifndef TESTS_CKSUM_H | ||
| 2 | #define TESTS_CKSUM_H | ||
| 3 | |||
| 4 | #include <stddef.h> | ||
| 5 | |||
| 6 | unsigned long cksum(const void *, size_t); | ||
| 7 | |||
| 8 | #endif /* tests/cksum.h */ | ||
diff --git a/pintos-progos/tests/cksum.pm b/pintos-progos/tests/cksum.pm new file mode 100644 index 0000000..73be5f2 --- /dev/null +++ b/pintos-progos/tests/cksum.pm | |||
| @@ -0,0 +1,87 @@ | |||
| 1 | # From the `cksum' entry in SUSv3. | ||
| 2 | |||
| 3 | use strict; | ||
| 4 | use warnings; | ||
| 5 | |||
| 6 | my (@crctab) = | ||
| 7 | (0x00000000, | ||
| 8 | 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, | ||
| 9 | 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, | ||
| 10 | 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, | ||
| 11 | 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, | ||
| 12 | 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, | ||
| 13 | 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, | ||
| 14 | 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, | ||
| 15 | 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, | ||
| 16 | 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, | ||
| 17 | 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, | ||
| 18 | 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, | ||
| 19 | 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, | ||
| 20 | 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, | ||
| 21 | 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, | ||
| 22 | 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, | ||
| 23 | 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, | ||
| 24 | 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, | ||
| 25 | 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, | ||
| 26 | 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, | ||
| 27 | 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, | ||
| 28 | 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, | ||
| 29 | 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, | ||
| 30 | 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, | ||
| 31 | 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f, | ||
| 32 | 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, | ||
| 33 | 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, | ||
| 34 | 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, | ||
| 35 | 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, | ||
| 36 | 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, | ||
| 37 | 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, | ||
| 38 | 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, | ||
| 39 | 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, | ||
| 40 | 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, | ||
| 41 | 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, | ||
| 42 | 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, | ||
| 43 | 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, | ||
| 44 | 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, | ||
| 45 | 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, | ||
| 46 | 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, | ||
| 47 | 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, | ||
| 48 | 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, | ||
| 49 | 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, | ||
| 50 | 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, | ||
| 51 | 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, | ||
| 52 | 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, | ||
| 53 | 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, | ||
| 54 | 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, | ||
| 55 | 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09, | ||
| 56 | 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, | ||
| 57 | 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, | ||
| 58 | 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4); | ||
| 59 | |||
| 60 | sub cksum { | ||
| 61 | my ($b) = @_; | ||
| 62 | my ($n) = length ($b); | ||
| 63 | my ($s) = 0; | ||
| 64 | for my $i (0...$n - 1) { | ||
| 65 | my ($c) = ord (substr ($b, $i, 1)); | ||
| 66 | $s = ($s << 8) ^ $crctab[($s >> 24) ^ $c]; | ||
| 67 | $s &= 0xffff_ffff; | ||
| 68 | } | ||
| 69 | while ($n != 0) { | ||
| 70 | my ($c) = $n & 0xff; | ||
| 71 | $n >>= 8; | ||
| 72 | $s = ($s << 8) ^ $crctab[($s >> 24) ^ $c]; | ||
| 73 | $s &= 0xffff_ffff; | ||
| 74 | } | ||
| 75 | return ~$s & 0xffff_ffff; | ||
| 76 | } | ||
| 77 | |||
| 78 | sub cksum_file { | ||
| 79 | my ($file) = @_; | ||
| 80 | open (FILE, '<', $file) or die "$file: open: $!\n"; | ||
| 81 | my ($data); | ||
| 82 | sysread (FILE, $data, -s FILE) == -s FILE or die "$file: read: $!\n"; | ||
| 83 | close (FILE); | ||
| 84 | return cksum ($data); | ||
| 85 | } | ||
| 86 | |||
| 87 | 1; | ||
diff --git a/pintos-progos/tests/filesys/Grading.no-vm b/pintos-progos/tests/filesys/Grading.no-vm new file mode 100644 index 0000000..ee98fc1 --- /dev/null +++ b/pintos-progos/tests/filesys/Grading.no-vm | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | # Percentage of the testing point total designated for each set of | ||
| 2 | # tests. | ||
| 3 | |||
| 4 | # This project is primarily about implementing the file system, but | ||
| 5 | # all the previous functionality should work too. It's not too easy | ||
| 6 | # to screw it up, thus the emphasis. | ||
| 7 | |||
| 8 | # 65% for extended file system features. | ||
| 9 | 30% tests/filesys/extended/Rubric.functionality | ||
| 10 | 15% tests/filesys/extended/Rubric.robustness | ||
| 11 | 20% tests/filesys/extended/Rubric.persistence | ||
| 12 | |||
| 13 | # 20% to not break the provided file system features. | ||
| 14 | 20% tests/filesys/base/Rubric | ||
| 15 | |||
| 16 | # 15% for the rest. | ||
| 17 | 10% tests/userprog/Rubric.functionality | ||
| 18 | 5% tests/userprog/Rubric.robustness | ||
diff --git a/pintos-progos/tests/filesys/Grading.with-vm b/pintos-progos/tests/filesys/Grading.with-vm new file mode 100644 index 0000000..e7c041e --- /dev/null +++ b/pintos-progos/tests/filesys/Grading.with-vm | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | # Percentage of the testing point total designated for each set of | ||
| 2 | # tests. | ||
| 3 | |||
| 4 | # This project is primarily about implementing the file system, but | ||
| 5 | # all the previous functionality should work too. It's not too easy | ||
| 6 | # to screw it up, thus the emphasis. | ||
| 7 | |||
| 8 | # 65% for extended file system features. | ||
| 9 | 30% tests/filesys/extended/Rubric.functionality | ||
| 10 | 15% tests/filesys/extended/Rubric.robustness | ||
| 11 | 20% tests/filesys/extended/Rubric.persistence | ||
| 12 | |||
| 13 | # 20% to not break the provided file system features. | ||
| 14 | 20% tests/filesys/base/Rubric | ||
| 15 | |||
| 16 | # 15% for the rest. | ||
| 17 | 10% tests/userprog/Rubric.functionality | ||
| 18 | 5% tests/userprog/Rubric.robustness | ||
| 19 | |||
| 20 | # Up to 10% bonus for working VM functionality. | ||
| 21 | 8% tests/vm/Rubric.functionality | ||
| 22 | 2% tests/vm/Rubric.robustness | ||
diff --git a/pintos-progos/tests/filesys/base/Make.tests b/pintos-progos/tests/filesys/base/Make.tests new file mode 100644 index 0000000..e475222 --- /dev/null +++ b/pintos-progos/tests/filesys/base/Make.tests | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | |||
| 3 | tests/filesys/base_TESTS = $(addprefix tests/filesys/base/,lg-create \ | ||
| 4 | lg-full lg-random lg-seq-block lg-seq-random sm-create sm-full \ | ||
| 5 | sm-random sm-seq-block sm-seq-random syn-read syn-remove syn-write) | ||
| 6 | |||
| 7 | tests/filesys/base_PROGS = $(tests/filesys/base_TESTS) $(addprefix \ | ||
| 8 | tests/filesys/base/,child-syn-read child-syn-wrt) | ||
| 9 | |||
| 10 | $(foreach prog,$(tests/filesys/base_PROGS), \ | ||
| 11 | $(eval $(prog)_SRC += $(prog).c tests/lib.c tests/filesys/seq-test.c)) | ||
| 12 | $(foreach prog,$(tests/filesys/base_TESTS), \ | ||
| 13 | $(eval $(prog)_SRC += tests/main.c)) | ||
| 14 | |||
| 15 | tests/filesys/base/syn-read_PUTFILES = tests/filesys/base/child-syn-read | ||
| 16 | tests/filesys/base/syn-write_PUTFILES = tests/filesys/base/child-syn-wrt | ||
| 17 | |||
| 18 | tests/filesys/base/syn-read.output: TIMEOUT = 300 | ||
diff --git a/pintos-progos/tests/filesys/base/Rubric b/pintos-progos/tests/filesys/base/Rubric new file mode 100644 index 0000000..49a9d15 --- /dev/null +++ b/pintos-progos/tests/filesys/base/Rubric | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | Functionality of base file system: | ||
| 2 | - Test basic support for small files. | ||
| 3 | 1 sm-create | ||
| 4 | 2 sm-full | ||
| 5 | 2 sm-random | ||
| 6 | 2 sm-seq-block | ||
| 7 | 3 sm-seq-random | ||
| 8 | |||
| 9 | - Test basic support for large files. | ||
| 10 | 1 lg-create | ||
| 11 | 2 lg-full | ||
| 12 | 2 lg-random | ||
| 13 | 2 lg-seq-block | ||
| 14 | 3 lg-seq-random | ||
| 15 | |||
| 16 | - Test synchronized multiprogram access to files. | ||
| 17 | 4 syn-read | ||
| 18 | 4 syn-write | ||
| 19 | 2 syn-remove | ||
diff --git a/pintos-progos/tests/filesys/base/child-syn-read.c b/pintos-progos/tests/filesys/base/child-syn-read.c new file mode 100644 index 0000000..77a5e26 --- /dev/null +++ b/pintos-progos/tests/filesys/base/child-syn-read.c | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | /* Child process for syn-read test. | ||
| 2 | Reads the contents of a test file a byte at a time, in the | ||
| 3 | hope that this will take long enough that we can get a | ||
| 4 | significant amount of contention in the kernel file system | ||
| 5 | code. */ | ||
| 6 | |||
| 7 | #include <random.h> | ||
| 8 | #include <stdio.h> | ||
| 9 | #include <stdlib.h> | ||
| 10 | #include <syscall.h> | ||
| 11 | #include "tests/lib.h" | ||
| 12 | #include "tests/filesys/base/syn-read.h" | ||
| 13 | |||
| 14 | const char *test_name = "child-syn-read"; | ||
| 15 | |||
| 16 | static char buf[BUF_SIZE]; | ||
| 17 | |||
| 18 | int | ||
| 19 | main (int argc, const char *argv[]) | ||
| 20 | { | ||
| 21 | int child_idx; | ||
| 22 | int fd; | ||
| 23 | size_t i; | ||
| 24 | |||
| 25 | quiet = true; | ||
| 26 | |||
| 27 | CHECK (argc == 2, "argc must be 2, actually %d", argc); | ||
| 28 | child_idx = atoi (argv[1]); | ||
| 29 | |||
| 30 | random_init (0); | ||
| 31 | random_bytes (buf, sizeof buf); | ||
| 32 | |||
| 33 | CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); | ||
| 34 | for (i = 0; i < sizeof buf; i++) | ||
| 35 | { | ||
| 36 | char c; | ||
| 37 | CHECK (read (fd, &c, 1) > 0, "read \"%s\"", file_name); | ||
| 38 | compare_bytes (&c, buf + i, 1, i, file_name); | ||
| 39 | } | ||
| 40 | close (fd); | ||
| 41 | |||
| 42 | return child_idx; | ||
| 43 | } | ||
| 44 | |||
diff --git a/pintos-progos/tests/filesys/base/child-syn-wrt.c b/pintos-progos/tests/filesys/base/child-syn-wrt.c new file mode 100644 index 0000000..1b52584 --- /dev/null +++ b/pintos-progos/tests/filesys/base/child-syn-wrt.c | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | /* Child process for syn-read test. | ||
| 2 | Writes into part of a test file. Other processes will be | ||
| 3 | writing into other parts at the same time. */ | ||
| 4 | |||
| 5 | #include <random.h> | ||
| 6 | #include <stdlib.h> | ||
| 7 | #include <syscall.h> | ||
| 8 | #include "tests/lib.h" | ||
| 9 | #include "tests/filesys/base/syn-write.h" | ||
| 10 | |||
| 11 | char buf[BUF_SIZE]; | ||
| 12 | |||
| 13 | int | ||
| 14 | main (int argc, char *argv[]) | ||
| 15 | { | ||
| 16 | int child_idx; | ||
| 17 | int fd; | ||
| 18 | |||
| 19 | quiet = true; | ||
| 20 | |||
| 21 | CHECK (argc == 2, "argc must be 2, actually %d", argc); | ||
| 22 | child_idx = atoi (argv[1]); | ||
| 23 | |||
| 24 | random_init (0); | ||
| 25 | random_bytes (buf, sizeof buf); | ||
| 26 | |||
| 27 | CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); | ||
| 28 | seek (fd, CHUNK_SIZE * child_idx); | ||
| 29 | CHECK (write (fd, buf + CHUNK_SIZE * child_idx, CHUNK_SIZE) > 0, | ||
| 30 | "write \"%s\"", file_name); | ||
| 31 | msg ("close \"%s\"", file_name); | ||
| 32 | close (fd); | ||
| 33 | |||
| 34 | return child_idx; | ||
| 35 | } | ||
diff --git a/pintos-progos/tests/filesys/base/full.inc b/pintos-progos/tests/filesys/base/full.inc new file mode 100644 index 0000000..38a0396 --- /dev/null +++ b/pintos-progos/tests/filesys/base/full.inc | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | /* -*- c -*- */ | ||
| 2 | |||
| 3 | #include "tests/filesys/seq-test.h" | ||
| 4 | #include "tests/main.h" | ||
| 5 | |||
| 6 | static char buf[TEST_SIZE]; | ||
| 7 | |||
| 8 | static size_t | ||
| 9 | return_test_size (void) | ||
| 10 | { | ||
| 11 | return TEST_SIZE; | ||
| 12 | } | ||
| 13 | |||
| 14 | void | ||
| 15 | test_main (void) | ||
| 16 | { | ||
| 17 | seq_test ("quux", | ||
| 18 | buf, sizeof buf, sizeof buf, | ||
| 19 | return_test_size, NULL); | ||
| 20 | } | ||
diff --git a/pintos-progos/tests/filesys/base/lg-create.c b/pintos-progos/tests/filesys/base/lg-create.c new file mode 100644 index 0000000..5c45eee --- /dev/null +++ b/pintos-progos/tests/filesys/base/lg-create.c | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | /* Tests that create properly zeros out the contents of a fairly | ||
| 2 | large file. */ | ||
| 3 | |||
| 4 | #define TEST_SIZE 75678 | ||
| 5 | #include "tests/filesys/create.inc" | ||
diff --git a/pintos-progos/tests/filesys/base/lg-create.ck b/pintos-progos/tests/filesys/base/lg-create.ck new file mode 100644 index 0000000..86b2c51 --- /dev/null +++ b/pintos-progos/tests/filesys/base/lg-create.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (lg-create) begin | ||
| 7 | (lg-create) create "blargle" | ||
| 8 | (lg-create) open "blargle" for verification | ||
| 9 | (lg-create) verified contents of "blargle" | ||
| 10 | (lg-create) close "blargle" | ||
| 11 | (lg-create) end | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/filesys/base/lg-full.c b/pintos-progos/tests/filesys/base/lg-full.c new file mode 100644 index 0000000..5f7234d --- /dev/null +++ b/pintos-progos/tests/filesys/base/lg-full.c | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | /* Writes out the contents of a fairly large file all at once, | ||
| 2 | and then reads it back to make sure that it was written | ||
| 3 | properly. */ | ||
| 4 | |||
| 5 | #define TEST_SIZE 75678 | ||
| 6 | #include "tests/filesys/base/full.inc" | ||
diff --git a/pintos-progos/tests/filesys/base/lg-full.ck b/pintos-progos/tests/filesys/base/lg-full.ck new file mode 100644 index 0000000..ee6c7f9 --- /dev/null +++ b/pintos-progos/tests/filesys/base/lg-full.ck | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (lg-full) begin | ||
| 7 | (lg-full) create "quux" | ||
| 8 | (lg-full) open "quux" | ||
| 9 | (lg-full) writing "quux" | ||
| 10 | (lg-full) close "quux" | ||
| 11 | (lg-full) open "quux" for verification | ||
| 12 | (lg-full) verified contents of "quux" | ||
| 13 | (lg-full) close "quux" | ||
| 14 | (lg-full) end | ||
| 15 | EOF | ||
| 16 | pass; | ||
diff --git a/pintos-progos/tests/filesys/base/lg-random.c b/pintos-progos/tests/filesys/base/lg-random.c new file mode 100644 index 0000000..b6f8873 --- /dev/null +++ b/pintos-progos/tests/filesys/base/lg-random.c | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | /* Writes out the content of a fairly large file in random order, | ||
| 2 | then reads it back in random order to verify that it was | ||
| 3 | written properly. */ | ||
| 4 | |||
| 5 | #define BLOCK_SIZE 512 | ||
| 6 | #define TEST_SIZE (512 * 150) | ||
| 7 | #include "tests/filesys/base/random.inc" | ||
diff --git a/pintos-progos/tests/filesys/base/lg-random.ck b/pintos-progos/tests/filesys/base/lg-random.ck new file mode 100644 index 0000000..dd9f1dd --- /dev/null +++ b/pintos-progos/tests/filesys/base/lg-random.ck | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (lg-random) begin | ||
| 7 | (lg-random) create "bazzle" | ||
| 8 | (lg-random) open "bazzle" | ||
| 9 | (lg-random) write "bazzle" in random order | ||
| 10 | (lg-random) read "bazzle" in random order | ||
| 11 | (lg-random) close "bazzle" | ||
| 12 | (lg-random) end | ||
| 13 | EOF | ||
| 14 | pass; | ||
diff --git a/pintos-progos/tests/filesys/base/lg-seq-block.c b/pintos-progos/tests/filesys/base/lg-seq-block.c new file mode 100644 index 0000000..580c30b --- /dev/null +++ b/pintos-progos/tests/filesys/base/lg-seq-block.c | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | /* Writes out a fairly large file sequentially, one fixed-size | ||
| 2 | block at a time, then reads it back to verify that it was | ||
| 3 | written properly. */ | ||
| 4 | |||
| 5 | #define TEST_SIZE 75678 | ||
| 6 | #define BLOCK_SIZE 513 | ||
| 7 | #include "tests/filesys/base/seq-block.inc" | ||
diff --git a/pintos-progos/tests/filesys/base/lg-seq-block.ck b/pintos-progos/tests/filesys/base/lg-seq-block.ck new file mode 100644 index 0000000..b789081 --- /dev/null +++ b/pintos-progos/tests/filesys/base/lg-seq-block.ck | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (lg-seq-block) begin | ||
| 7 | (lg-seq-block) create "noodle" | ||
| 8 | (lg-seq-block) open "noodle" | ||
| 9 | (lg-seq-block) writing "noodle" | ||
| 10 | (lg-seq-block) close "noodle" | ||
| 11 | (lg-seq-block) open "noodle" for verification | ||
| 12 | (lg-seq-block) verified contents of "noodle" | ||
| 13 | (lg-seq-block) close "noodle" | ||
| 14 | (lg-seq-block) end | ||
| 15 | EOF | ||
| 16 | pass; | ||
diff --git a/pintos-progos/tests/filesys/base/lg-seq-random.c b/pintos-progos/tests/filesys/base/lg-seq-random.c new file mode 100644 index 0000000..fbb6bba --- /dev/null +++ b/pintos-progos/tests/filesys/base/lg-seq-random.c | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | /* Writes out a fairly large file sequentially, one random-sized | ||
| 2 | block at a time, then reads it back to verify that it was | ||
| 3 | written properly. */ | ||
| 4 | |||
| 5 | #define TEST_SIZE 75678 | ||
| 6 | #include "tests/filesys/base/seq-random.inc" | ||
diff --git a/pintos-progos/tests/filesys/base/lg-seq-random.ck b/pintos-progos/tests/filesys/base/lg-seq-random.ck new file mode 100644 index 0000000..6b2dc82 --- /dev/null +++ b/pintos-progos/tests/filesys/base/lg-seq-random.ck | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (lg-seq-random) begin | ||
| 7 | (lg-seq-random) create "nibble" | ||
| 8 | (lg-seq-random) open "nibble" | ||
| 9 | (lg-seq-random) writing "nibble" | ||
| 10 | (lg-seq-random) close "nibble" | ||
| 11 | (lg-seq-random) open "nibble" for verification | ||
| 12 | (lg-seq-random) verified contents of "nibble" | ||
| 13 | (lg-seq-random) close "nibble" | ||
| 14 | (lg-seq-random) end | ||
| 15 | EOF | ||
| 16 | pass; | ||
diff --git a/pintos-progos/tests/filesys/base/random.inc b/pintos-progos/tests/filesys/base/random.inc new file mode 100644 index 0000000..eeeea68 --- /dev/null +++ b/pintos-progos/tests/filesys/base/random.inc | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | /* -*- c -*- */ | ||
| 2 | |||
| 3 | #include <random.h> | ||
| 4 | #include <stdio.h> | ||
| 5 | #include <string.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | #if TEST_SIZE % BLOCK_SIZE != 0 | ||
| 11 | #error TEST_SIZE must be a multiple of BLOCK_SIZE | ||
| 12 | #endif | ||
| 13 | |||
| 14 | #define BLOCK_CNT (TEST_SIZE / BLOCK_SIZE) | ||
| 15 | |||
| 16 | char buf[TEST_SIZE]; | ||
| 17 | int order[BLOCK_CNT]; | ||
| 18 | |||
| 19 | void | ||
| 20 | test_main (void) | ||
| 21 | { | ||
| 22 | const char *file_name = "bazzle"; | ||
| 23 | int fd; | ||
| 24 | size_t i; | ||
| 25 | |||
| 26 | random_init (57); | ||
| 27 | random_bytes (buf, sizeof buf); | ||
| 28 | |||
| 29 | for (i = 0; i < BLOCK_CNT; i++) | ||
| 30 | order[i] = i; | ||
| 31 | |||
| 32 | CHECK (create (file_name, TEST_SIZE), "create \"%s\"", file_name); | ||
| 33 | CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); | ||
| 34 | |||
| 35 | msg ("write \"%s\" in random order", file_name); | ||
| 36 | shuffle (order, BLOCK_CNT, sizeof *order); | ||
| 37 | for (i = 0; i < BLOCK_CNT; i++) | ||
| 38 | { | ||
| 39 | size_t ofs = BLOCK_SIZE * order[i]; | ||
| 40 | seek (fd, ofs); | ||
| 41 | if (write (fd, buf + ofs, BLOCK_SIZE) != BLOCK_SIZE) | ||
| 42 | fail ("write %d bytes at offset %zu failed", (int) BLOCK_SIZE, ofs); | ||
| 43 | } | ||
| 44 | |||
| 45 | msg ("read \"%s\" in random order", file_name); | ||
| 46 | shuffle (order, BLOCK_CNT, sizeof *order); | ||
| 47 | for (i = 0; i < BLOCK_CNT; i++) | ||
| 48 | { | ||
| 49 | char block[BLOCK_SIZE]; | ||
| 50 | size_t ofs = BLOCK_SIZE * order[i]; | ||
| 51 | seek (fd, ofs); | ||
| 52 | if (read (fd, block, BLOCK_SIZE) != BLOCK_SIZE) | ||
| 53 | fail ("read %d bytes at offset %zu failed", (int) BLOCK_SIZE, ofs); | ||
| 54 | compare_bytes (block, buf + ofs, BLOCK_SIZE, ofs, file_name); | ||
| 55 | } | ||
| 56 | |||
| 57 | msg ("close \"%s\"", file_name); | ||
| 58 | close (fd); | ||
| 59 | } | ||
diff --git a/pintos-progos/tests/filesys/base/seq-block.inc b/pintos-progos/tests/filesys/base/seq-block.inc new file mode 100644 index 0000000..d4c1f57 --- /dev/null +++ b/pintos-progos/tests/filesys/base/seq-block.inc | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | /* -*- c -*- */ | ||
| 2 | |||
| 3 | #include "tests/filesys/seq-test.h" | ||
| 4 | #include "tests/main.h" | ||
| 5 | |||
| 6 | static char buf[TEST_SIZE]; | ||
| 7 | |||
| 8 | static size_t | ||
| 9 | return_block_size (void) | ||
| 10 | { | ||
| 11 | return BLOCK_SIZE; | ||
| 12 | } | ||
| 13 | |||
| 14 | void | ||
| 15 | test_main (void) | ||
| 16 | { | ||
| 17 | seq_test ("noodle", | ||
| 18 | buf, sizeof buf, sizeof buf, | ||
| 19 | return_block_size, NULL); | ||
| 20 | } | ||
diff --git a/pintos-progos/tests/filesys/base/seq-random.inc b/pintos-progos/tests/filesys/base/seq-random.inc new file mode 100644 index 0000000..a4da4c5 --- /dev/null +++ b/pintos-progos/tests/filesys/base/seq-random.inc | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | /* -*- c -*- */ | ||
| 2 | |||
| 3 | #include <random.h> | ||
| 4 | #include "tests/filesys/seq-test.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | static char buf[TEST_SIZE]; | ||
| 8 | |||
| 9 | static size_t | ||
| 10 | return_random (void) | ||
| 11 | { | ||
| 12 | return random_ulong () % 1031 + 1; | ||
| 13 | } | ||
| 14 | |||
| 15 | void | ||
| 16 | test_main (void) | ||
| 17 | { | ||
| 18 | random_init (-1); | ||
| 19 | seq_test ("nibble", | ||
| 20 | buf, sizeof buf, sizeof buf, | ||
| 21 | return_random, NULL); | ||
| 22 | } | ||
diff --git a/pintos-progos/tests/filesys/base/sm-create.c b/pintos-progos/tests/filesys/base/sm-create.c new file mode 100644 index 0000000..6b97ac1 --- /dev/null +++ b/pintos-progos/tests/filesys/base/sm-create.c | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | /* Tests that create properly zeros out the contents of a fairly | ||
| 2 | small file. */ | ||
| 3 | |||
| 4 | #define TEST_SIZE 5678 | ||
| 5 | #include "tests/filesys/create.inc" | ||
diff --git a/pintos-progos/tests/filesys/base/sm-create.ck b/pintos-progos/tests/filesys/base/sm-create.ck new file mode 100644 index 0000000..8ca80dc --- /dev/null +++ b/pintos-progos/tests/filesys/base/sm-create.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (sm-create) begin | ||
| 7 | (sm-create) create "blargle" | ||
| 8 | (sm-create) open "blargle" for verification | ||
| 9 | (sm-create) verified contents of "blargle" | ||
| 10 | (sm-create) close "blargle" | ||
| 11 | (sm-create) end | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/filesys/base/sm-full.c b/pintos-progos/tests/filesys/base/sm-full.c new file mode 100644 index 0000000..23ff3d4 --- /dev/null +++ b/pintos-progos/tests/filesys/base/sm-full.c | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | /* Writes out the contents of a fairly small file all at once, | ||
| 2 | and then reads it back to make sure that it was written | ||
| 3 | properly. */ | ||
| 4 | |||
| 5 | #define TEST_SIZE 5678 | ||
| 6 | #include "tests/filesys/base/full.inc" | ||
diff --git a/pintos-progos/tests/filesys/base/sm-full.ck b/pintos-progos/tests/filesys/base/sm-full.ck new file mode 100644 index 0000000..2e0eb36 --- /dev/null +++ b/pintos-progos/tests/filesys/base/sm-full.ck | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (sm-full) begin | ||
| 7 | (sm-full) create "quux" | ||
| 8 | (sm-full) open "quux" | ||
| 9 | (sm-full) writing "quux" | ||
| 10 | (sm-full) close "quux" | ||
| 11 | (sm-full) open "quux" for verification | ||
| 12 | (sm-full) verified contents of "quux" | ||
| 13 | (sm-full) close "quux" | ||
| 14 | (sm-full) end | ||
| 15 | EOF | ||
| 16 | pass; | ||
diff --git a/pintos-progos/tests/filesys/base/sm-random.c b/pintos-progos/tests/filesys/base/sm-random.c new file mode 100644 index 0000000..42d670f --- /dev/null +++ b/pintos-progos/tests/filesys/base/sm-random.c | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | /* Writes out the content of a fairly small file in random order, | ||
| 2 | then reads it back in random order to verify that it was | ||
| 3 | written properly. */ | ||
| 4 | |||
| 5 | #define BLOCK_SIZE 13 | ||
| 6 | #define TEST_SIZE (13 * 123) | ||
| 7 | #include "tests/filesys/base/random.inc" | ||
diff --git a/pintos-progos/tests/filesys/base/sm-random.ck b/pintos-progos/tests/filesys/base/sm-random.ck new file mode 100644 index 0000000..bda049d --- /dev/null +++ b/pintos-progos/tests/filesys/base/sm-random.ck | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (sm-random) begin | ||
| 7 | (sm-random) create "bazzle" | ||
| 8 | (sm-random) open "bazzle" | ||
| 9 | (sm-random) write "bazzle" in random order | ||
| 10 | (sm-random) read "bazzle" in random order | ||
| 11 | (sm-random) close "bazzle" | ||
| 12 | (sm-random) end | ||
| 13 | EOF | ||
| 14 | pass; | ||
diff --git a/pintos-progos/tests/filesys/base/sm-seq-block.c b/pintos-progos/tests/filesys/base/sm-seq-block.c new file mode 100644 index 0000000..e368327 --- /dev/null +++ b/pintos-progos/tests/filesys/base/sm-seq-block.c | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | /* Writes out a fairly small file sequentially, one fixed-size | ||
| 2 | block at a time, then reads it back to verify that it was | ||
| 3 | written properly. */ | ||
| 4 | |||
| 5 | #define TEST_SIZE 5678 | ||
| 6 | #define BLOCK_SIZE 513 | ||
| 7 | #include "tests/filesys/base/seq-block.inc" | ||
diff --git a/pintos-progos/tests/filesys/base/sm-seq-block.ck b/pintos-progos/tests/filesys/base/sm-seq-block.ck new file mode 100644 index 0000000..0e2939d --- /dev/null +++ b/pintos-progos/tests/filesys/base/sm-seq-block.ck | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (sm-seq-block) begin | ||
| 7 | (sm-seq-block) create "noodle" | ||
| 8 | (sm-seq-block) open "noodle" | ||
| 9 | (sm-seq-block) writing "noodle" | ||
| 10 | (sm-seq-block) close "noodle" | ||
| 11 | (sm-seq-block) open "noodle" for verification | ||
| 12 | (sm-seq-block) verified contents of "noodle" | ||
| 13 | (sm-seq-block) close "noodle" | ||
| 14 | (sm-seq-block) end | ||
| 15 | EOF | ||
| 16 | pass; | ||
diff --git a/pintos-progos/tests/filesys/base/sm-seq-random.c b/pintos-progos/tests/filesys/base/sm-seq-random.c new file mode 100644 index 0000000..89e5b71 --- /dev/null +++ b/pintos-progos/tests/filesys/base/sm-seq-random.c | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | /* Writes out a fairly large file sequentially, one random-sized | ||
| 2 | block at a time, then reads it back to verify that it was | ||
| 3 | written properly. */ | ||
| 4 | |||
| 5 | #define TEST_SIZE 5678 | ||
| 6 | #include "tests/filesys/base/seq-random.inc" | ||
diff --git a/pintos-progos/tests/filesys/base/sm-seq-random.ck b/pintos-progos/tests/filesys/base/sm-seq-random.ck new file mode 100644 index 0000000..2fb368b --- /dev/null +++ b/pintos-progos/tests/filesys/base/sm-seq-random.ck | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (sm-seq-random) begin | ||
| 7 | (sm-seq-random) create "nibble" | ||
| 8 | (sm-seq-random) open "nibble" | ||
| 9 | (sm-seq-random) writing "nibble" | ||
| 10 | (sm-seq-random) close "nibble" | ||
| 11 | (sm-seq-random) open "nibble" for verification | ||
| 12 | (sm-seq-random) verified contents of "nibble" | ||
| 13 | (sm-seq-random) close "nibble" | ||
| 14 | (sm-seq-random) end | ||
| 15 | EOF | ||
| 16 | pass; | ||
diff --git a/pintos-progos/tests/filesys/base/syn-read.c b/pintos-progos/tests/filesys/base/syn-read.c new file mode 100644 index 0000000..7c36a42 --- /dev/null +++ b/pintos-progos/tests/filesys/base/syn-read.c | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | /* Spawns 10 child processes, all of which read from the same | ||
| 2 | file and make sure that the contents are what they should | ||
| 3 | be. */ | ||
| 4 | |||
| 5 | #include <random.h> | ||
| 6 | #include <stdio.h> | ||
| 7 | #include <syscall.h> | ||
| 8 | #include "tests/lib.h" | ||
| 9 | #include "tests/main.h" | ||
| 10 | #include "tests/filesys/base/syn-read.h" | ||
| 11 | |||
| 12 | static char buf[BUF_SIZE]; | ||
| 13 | |||
| 14 | #define CHILD_CNT 10 | ||
| 15 | |||
| 16 | void | ||
| 17 | test_main (void) | ||
| 18 | { | ||
| 19 | pid_t children[CHILD_CNT]; | ||
| 20 | int fd; | ||
| 21 | |||
| 22 | CHECK (create (file_name, sizeof buf), "create \"%s\"", file_name); | ||
| 23 | CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); | ||
| 24 | random_bytes (buf, sizeof buf); | ||
| 25 | CHECK (write (fd, buf, sizeof buf) > 0, "write \"%s\"", file_name); | ||
| 26 | msg ("close \"%s\"", file_name); | ||
| 27 | close (fd); | ||
| 28 | |||
| 29 | exec_children ("child-syn-read", children, CHILD_CNT); | ||
| 30 | wait_children (children, CHILD_CNT); | ||
| 31 | } | ||
diff --git a/pintos-progos/tests/filesys/base/syn-read.ck b/pintos-progos/tests/filesys/base/syn-read.ck new file mode 100644 index 0000000..e2f68e8 --- /dev/null +++ b/pintos-progos/tests/filesys/base/syn-read.ck | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (syn-read) begin | ||
| 7 | (syn-read) create "data" | ||
| 8 | (syn-read) open "data" | ||
| 9 | (syn-read) write "data" | ||
| 10 | (syn-read) close "data" | ||
| 11 | (syn-read) exec child 1 of 10: "child-syn-read 0" | ||
| 12 | (syn-read) exec child 2 of 10: "child-syn-read 1" | ||
| 13 | (syn-read) exec child 3 of 10: "child-syn-read 2" | ||
| 14 | (syn-read) exec child 4 of 10: "child-syn-read 3" | ||
| 15 | (syn-read) exec child 5 of 10: "child-syn-read 4" | ||
| 16 | (syn-read) exec child 6 of 10: "child-syn-read 5" | ||
| 17 | (syn-read) exec child 7 of 10: "child-syn-read 6" | ||
| 18 | (syn-read) exec child 8 of 10: "child-syn-read 7" | ||
| 19 | (syn-read) exec child 9 of 10: "child-syn-read 8" | ||
| 20 | (syn-read) exec child 10 of 10: "child-syn-read 9" | ||
| 21 | (syn-read) wait for child 1 of 10 returned 0 (expected 0) | ||
| 22 | (syn-read) wait for child 2 of 10 returned 1 (expected 1) | ||
| 23 | (syn-read) wait for child 3 of 10 returned 2 (expected 2) | ||
| 24 | (syn-read) wait for child 4 of 10 returned 3 (expected 3) | ||
| 25 | (syn-read) wait for child 5 of 10 returned 4 (expected 4) | ||
| 26 | (syn-read) wait for child 6 of 10 returned 5 (expected 5) | ||
| 27 | (syn-read) wait for child 7 of 10 returned 6 (expected 6) | ||
| 28 | (syn-read) wait for child 8 of 10 returned 7 (expected 7) | ||
| 29 | (syn-read) wait for child 9 of 10 returned 8 (expected 8) | ||
| 30 | (syn-read) wait for child 10 of 10 returned 9 (expected 9) | ||
| 31 | (syn-read) end | ||
| 32 | EOF | ||
| 33 | pass; | ||
diff --git a/pintos-progos/tests/filesys/base/syn-read.h b/pintos-progos/tests/filesys/base/syn-read.h new file mode 100644 index 0000000..bff8082 --- /dev/null +++ b/pintos-progos/tests/filesys/base/syn-read.h | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | #ifndef TESTS_FILESYS_BASE_SYN_READ_H | ||
| 2 | #define TESTS_FILESYS_BASE_SYN_READ_H | ||
| 3 | |||
| 4 | #define BUF_SIZE 1024 | ||
| 5 | static const char file_name[] = "data"; | ||
| 6 | |||
| 7 | #endif /* tests/filesys/base/syn-read.h */ | ||
diff --git a/pintos-progos/tests/filesys/base/syn-remove.c b/pintos-progos/tests/filesys/base/syn-remove.c new file mode 100644 index 0000000..c9ba110 --- /dev/null +++ b/pintos-progos/tests/filesys/base/syn-remove.c | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | /* Verifies that a deleted file may still be written to and read | ||
| 2 | from. */ | ||
| 3 | |||
| 4 | #include <random.h> | ||
| 5 | #include <string.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | char buf1[1234]; | ||
| 11 | char buf2[1234]; | ||
| 12 | |||
| 13 | void | ||
| 14 | test_main (void) | ||
| 15 | { | ||
| 16 | const char *file_name = "deleteme"; | ||
| 17 | int fd; | ||
| 18 | |||
| 19 | CHECK (create (file_name, sizeof buf1), "create \"%s\"", file_name); | ||
| 20 | CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); | ||
| 21 | CHECK (remove (file_name), "remove \"%s\"", file_name); | ||
| 22 | random_bytes (buf1, sizeof buf1); | ||
| 23 | CHECK (write (fd, buf1, sizeof buf1) > 0, "write \"%s\"", file_name); | ||
| 24 | msg ("seek \"%s\" to 0", file_name); | ||
| 25 | seek (fd, 0); | ||
| 26 | CHECK (read (fd, buf2, sizeof buf2) > 0, "read \"%s\"", file_name); | ||
| 27 | compare_bytes (buf2, buf1, sizeof buf1, 0, file_name); | ||
| 28 | msg ("close \"%s\"", file_name); | ||
| 29 | close (fd); | ||
| 30 | } | ||
diff --git a/pintos-progos/tests/filesys/base/syn-remove.ck b/pintos-progos/tests/filesys/base/syn-remove.ck new file mode 100644 index 0000000..16ff11e --- /dev/null +++ b/pintos-progos/tests/filesys/base/syn-remove.ck | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (syn-remove) begin | ||
| 7 | (syn-remove) create "deleteme" | ||
| 8 | (syn-remove) open "deleteme" | ||
| 9 | (syn-remove) remove "deleteme" | ||
| 10 | (syn-remove) write "deleteme" | ||
| 11 | (syn-remove) seek "deleteme" to 0 | ||
| 12 | (syn-remove) read "deleteme" | ||
| 13 | (syn-remove) close "deleteme" | ||
| 14 | (syn-remove) end | ||
| 15 | EOF | ||
| 16 | pass; | ||
diff --git a/pintos-progos/tests/filesys/base/syn-write.c b/pintos-progos/tests/filesys/base/syn-write.c new file mode 100644 index 0000000..1439862 --- /dev/null +++ b/pintos-progos/tests/filesys/base/syn-write.c | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | /* Spawns several child processes to write out different parts of | ||
| 2 | the contents of a file and waits for them to finish. Then | ||
| 3 | reads back the file and verifies its contents. */ | ||
| 4 | |||
| 5 | #include <random.h> | ||
| 6 | #include <stdio.h> | ||
| 7 | #include <string.h> | ||
| 8 | #include <syscall.h> | ||
| 9 | #include "tests/filesys/base/syn-write.h" | ||
| 10 | #include "tests/lib.h" | ||
| 11 | #include "tests/main.h" | ||
| 12 | |||
| 13 | char buf1[BUF_SIZE]; | ||
| 14 | char buf2[BUF_SIZE]; | ||
| 15 | |||
| 16 | void | ||
| 17 | test_main (void) | ||
| 18 | { | ||
| 19 | pid_t children[CHILD_CNT]; | ||
| 20 | int fd; | ||
| 21 | |||
| 22 | CHECK (create (file_name, sizeof buf1), "create \"%s\"", file_name); | ||
| 23 | |||
| 24 | exec_children ("child-syn-wrt", children, CHILD_CNT); | ||
| 25 | wait_children (children, CHILD_CNT); | ||
| 26 | |||
| 27 | CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); | ||
| 28 | CHECK (read (fd, buf1, sizeof buf1) > 0, "read \"%s\"", file_name); | ||
| 29 | random_bytes (buf2, sizeof buf2); | ||
| 30 | compare_bytes (buf1, buf2, sizeof buf1, 0, file_name); | ||
| 31 | } | ||
diff --git a/pintos-progos/tests/filesys/base/syn-write.ck b/pintos-progos/tests/filesys/base/syn-write.ck new file mode 100644 index 0000000..629a7a2 --- /dev/null +++ b/pintos-progos/tests/filesys/base/syn-write.ck | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (syn-write) begin | ||
| 7 | (syn-write) create "stuff" | ||
| 8 | (syn-write) exec child 1 of 10: "child-syn-wrt 0" | ||
| 9 | (syn-write) exec child 2 of 10: "child-syn-wrt 1" | ||
| 10 | (syn-write) exec child 3 of 10: "child-syn-wrt 2" | ||
| 11 | (syn-write) exec child 4 of 10: "child-syn-wrt 3" | ||
| 12 | (syn-write) exec child 5 of 10: "child-syn-wrt 4" | ||
| 13 | (syn-write) exec child 6 of 10: "child-syn-wrt 5" | ||
| 14 | (syn-write) exec child 7 of 10: "child-syn-wrt 6" | ||
| 15 | (syn-write) exec child 8 of 10: "child-syn-wrt 7" | ||
| 16 | (syn-write) exec child 9 of 10: "child-syn-wrt 8" | ||
| 17 | (syn-write) exec child 10 of 10: "child-syn-wrt 9" | ||
| 18 | (syn-write) wait for child 1 of 10 returned 0 (expected 0) | ||
| 19 | (syn-write) wait for child 2 of 10 returned 1 (expected 1) | ||
| 20 | (syn-write) wait for child 3 of 10 returned 2 (expected 2) | ||
| 21 | (syn-write) wait for child 4 of 10 returned 3 (expected 3) | ||
| 22 | (syn-write) wait for child 5 of 10 returned 4 (expected 4) | ||
| 23 | (syn-write) wait for child 6 of 10 returned 5 (expected 5) | ||
| 24 | (syn-write) wait for child 7 of 10 returned 6 (expected 6) | ||
| 25 | (syn-write) wait for child 8 of 10 returned 7 (expected 7) | ||
| 26 | (syn-write) wait for child 9 of 10 returned 8 (expected 8) | ||
| 27 | (syn-write) wait for child 10 of 10 returned 9 (expected 9) | ||
| 28 | (syn-write) open "stuff" | ||
| 29 | (syn-write) read "stuff" | ||
| 30 | (syn-write) end | ||
| 31 | EOF | ||
| 32 | pass; | ||
diff --git a/pintos-progos/tests/filesys/base/syn-write.h b/pintos-progos/tests/filesys/base/syn-write.h new file mode 100644 index 0000000..07a6d5a --- /dev/null +++ b/pintos-progos/tests/filesys/base/syn-write.h | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | #ifndef TESTS_FILESYS_BASE_SYN_WRITE_H | ||
| 2 | #define TESTS_FILESYS_BASE_SYN_WRITE_H | ||
| 3 | |||
| 4 | #define CHILD_CNT 10 | ||
| 5 | #define CHUNK_SIZE 512 | ||
| 6 | #define BUF_SIZE (CHILD_CNT * CHUNK_SIZE) | ||
| 7 | static const char file_name[] = "stuff"; | ||
| 8 | |||
| 9 | #endif /* tests/filesys/base/syn-write.h */ | ||
diff --git a/pintos-progos/tests/filesys/create.inc b/pintos-progos/tests/filesys/create.inc new file mode 100644 index 0000000..4baf771 --- /dev/null +++ b/pintos-progos/tests/filesys/create.inc | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | /* -*- c -*- */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | static char buf[TEST_SIZE]; | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | const char *file_name = "blargle"; | ||
| 13 | CHECK (create (file_name, TEST_SIZE), "create \"%s\"", file_name); | ||
| 14 | check_file (file_name, buf, TEST_SIZE); | ||
| 15 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/Make.tests b/pintos-progos/tests/filesys/extended/Make.tests new file mode 100644 index 0000000..e03b98d --- /dev/null +++ b/pintos-progos/tests/filesys/extended/Make.tests | |||
| @@ -0,0 +1,61 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | |||
| 3 | raw_tests = dir-empty-name dir-mk-tree dir-mkdir dir-open \ | ||
| 4 | dir-over-file dir-rm-cwd dir-rm-parent dir-rm-root dir-rm-tree \ | ||
| 5 | dir-rmdir dir-under-file dir-vine grow-create grow-dir-lg \ | ||
| 6 | grow-file-size grow-root-lg grow-root-sm grow-seq-lg grow-seq-sm \ | ||
| 7 | grow-sparse grow-tell grow-two-files syn-rw | ||
| 8 | |||
| 9 | tests/filesys/extended_TESTS = $(patsubst %,tests/filesys/extended/%,$(raw_tests)) | ||
| 10 | tests/filesys/extended_EXTRA_GRADES = $(patsubst %,tests/filesys/extended/%-persistence,$(raw_tests)) | ||
| 11 | |||
| 12 | tests/filesys/extended_PROGS = $(tests/filesys/extended_TESTS) \ | ||
| 13 | tests/filesys/extended/child-syn-rw tests/filesys/extended/tar | ||
| 14 | |||
| 15 | $(foreach prog,$(tests/filesys/extended_PROGS), \ | ||
| 16 | $(eval $(prog)_SRC += $(prog).c tests/lib.c tests/filesys/seq-test.c)) | ||
| 17 | $(foreach prog,$(tests/filesys/extended_TESTS), \ | ||
| 18 | $(eval $(prog)_SRC += tests/main.c)) | ||
| 19 | $(foreach prog,$(tests/filesys/extended_TESTS), \ | ||
| 20 | $(eval $(prog)_PUTFILES += tests/filesys/extended/tar)) | ||
| 21 | # The version of GNU make 3.80 on vine barfs if this is split at | ||
| 22 | # the last comma. | ||
| 23 | $(foreach test,$(tests/filesys/extended_TESTS),$(eval $(test).output: FILESYSSOURCE = --disk=tmp.dsk)) | ||
| 24 | |||
| 25 | tests/filesys/extended/dir-mk-tree_SRC += tests/filesys/extended/mk-tree.c | ||
| 26 | tests/filesys/extended/dir-rm-tree_SRC += tests/filesys/extended/mk-tree.c | ||
| 27 | |||
| 28 | tests/filesys/extended/syn-rw_PUTFILES += tests/filesys/extended/child-syn-rw | ||
| 29 | |||
| 30 | tests/filesys/extended/dir-vine.output: TIMEOUT = 150 | ||
| 31 | |||
| 32 | GETTIMEOUT = 60 | ||
| 33 | |||
| 34 | GETCMD = pintos -v -k -T $(GETTIMEOUT) | ||
| 35 | GETCMD += $(PINTOSOPTS) | ||
| 36 | GETCMD += $(SIMULATOR) | ||
| 37 | GETCMD += $(FILESYSSOURCE) | ||
| 38 | GETCMD += -g fs.tar -a $(TEST).tar | ||
| 39 | ifeq ($(filter vm, $(KERNEL_SUBDIRS)), vm) | ||
| 40 | GETCMD += --swap-size=4 | ||
| 41 | endif | ||
| 42 | GETCMD += -- -q | ||
| 43 | GETCMD += $(KERNELFLAGS) | ||
| 44 | GETCMD += run 'tar fs.tar /' | ||
| 45 | GETCMD += < /dev/null | ||
| 46 | GETCMD += 2> $(TEST)-persistence.errors $(if $(VERBOSE),|tee,>) $(TEST)-persistence.output | ||
| 47 | |||
| 48 | tests/filesys/extended/%.output: kernel.bin | ||
| 49 | rm -f tmp.dsk | ||
| 50 | pintos-mkdisk tmp.dsk --filesys-size=2 | ||
| 51 | $(TESTCMD) | ||
| 52 | $(GETCMD) | ||
| 53 | rm -f tmp.dsk | ||
| 54 | $(foreach raw_test,$(raw_tests),$(eval tests/filesys/extended/$(raw_test)-persistence.output: tests/filesys/extended/$(raw_test).output)) | ||
| 55 | $(foreach raw_test,$(raw_tests),$(eval tests/filesys/extended/$(raw_test)-persistence.result: tests/filesys/extended/$(raw_test).result)) | ||
| 56 | |||
| 57 | TARS = $(addsuffix .tar,$(tests/filesys/extended_TESTS)) | ||
| 58 | |||
| 59 | clean:: | ||
| 60 | rm -f $(TARS) | ||
| 61 | rm -f tests/filesys/extended/can-rmdir-cwd | ||
diff --git a/pintos-progos/tests/filesys/extended/Rubric.functionality b/pintos-progos/tests/filesys/extended/Rubric.functionality new file mode 100644 index 0000000..91ed6f0 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/Rubric.functionality | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | Functionality of extended file system: | ||
| 2 | - Test directory support. | ||
| 3 | 1 dir-mkdir | ||
| 4 | 3 dir-mk-tree | ||
| 5 | |||
| 6 | 1 dir-rmdir | ||
| 7 | 3 dir-rm-tree | ||
| 8 | |||
| 9 | 5 dir-vine | ||
| 10 | |||
| 11 | - Test file growth. | ||
| 12 | 1 grow-create | ||
| 13 | 1 grow-seq-sm | ||
| 14 | 3 grow-seq-lg | ||
| 15 | 3 grow-sparse | ||
| 16 | 3 grow-two-files | ||
| 17 | 1 grow-tell | ||
| 18 | 1 grow-file-size | ||
| 19 | |||
| 20 | - Test directory growth. | ||
| 21 | 1 grow-dir-lg | ||
| 22 | 1 grow-root-sm | ||
| 23 | 1 grow-root-lg | ||
| 24 | |||
| 25 | - Test writing from multiple processes. | ||
| 26 | 5 syn-rw | ||
diff --git a/pintos-progos/tests/filesys/extended/Rubric.persistence b/pintos-progos/tests/filesys/extended/Rubric.persistence new file mode 100644 index 0000000..405620a --- /dev/null +++ b/pintos-progos/tests/filesys/extended/Rubric.persistence | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | Persistence of file system: | ||
| 2 | 1 dir-empty-name-persistence | ||
| 3 | 1 dir-mk-tree-persistence | ||
| 4 | 1 dir-mkdir-persistence | ||
| 5 | 1 dir-open-persistence | ||
| 6 | 1 dir-over-file-persistence | ||
| 7 | 1 dir-rm-cwd-persistence | ||
| 8 | 1 dir-rm-parent-persistence | ||
| 9 | 1 dir-rm-root-persistence | ||
| 10 | 1 dir-rm-tree-persistence | ||
| 11 | 1 dir-rmdir-persistence | ||
| 12 | 1 dir-under-file-persistence | ||
| 13 | 1 dir-vine-persistence | ||
| 14 | 1 grow-create-persistence | ||
| 15 | 1 grow-dir-lg-persistence | ||
| 16 | 1 grow-file-size-persistence | ||
| 17 | 1 grow-root-lg-persistence | ||
| 18 | 1 grow-root-sm-persistence | ||
| 19 | 1 grow-seq-lg-persistence | ||
| 20 | 1 grow-seq-sm-persistence | ||
| 21 | 1 grow-sparse-persistence | ||
| 22 | 1 grow-tell-persistence | ||
| 23 | 1 grow-two-files-persistence | ||
| 24 | 1 syn-rw-persistence | ||
diff --git a/pintos-progos/tests/filesys/extended/Rubric.robustness b/pintos-progos/tests/filesys/extended/Rubric.robustness new file mode 100644 index 0000000..fb9f32f --- /dev/null +++ b/pintos-progos/tests/filesys/extended/Rubric.robustness | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | Robustness of file system: | ||
| 2 | 1 dir-empty-name | ||
| 3 | 1 dir-open | ||
| 4 | 1 dir-over-file | ||
| 5 | 1 dir-under-file | ||
| 6 | |||
| 7 | 3 dir-rm-cwd | ||
| 8 | 2 dir-rm-parent | ||
| 9 | 1 dir-rm-root | ||
diff --git a/pintos-progos/tests/filesys/extended/child-syn-rw.c b/pintos-progos/tests/filesys/extended/child-syn-rw.c new file mode 100644 index 0000000..0e2217d --- /dev/null +++ b/pintos-progos/tests/filesys/extended/child-syn-rw.c | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | /* Child process for syn-rw. | ||
| 2 | Reads from a file created by our parent process, which is | ||
| 3 | growing it. We loop until we've read the whole file | ||
| 4 | successfully. Many iterations through the loop will return 0 | ||
| 5 | bytes, because the file has not grown in the meantime. That | ||
| 6 | is, we are "busy waiting" for the file to grow. | ||
| 7 | (This test could be improved by adding a "yield" system call | ||
| 8 | and calling yield whenever we receive a 0-byte read.) */ | ||
| 9 | |||
| 10 | #include <random.h> | ||
| 11 | #include <stdlib.h> | ||
| 12 | #include <syscall.h> | ||
| 13 | #include "tests/filesys/extended/syn-rw.h" | ||
| 14 | #include "tests/lib.h" | ||
| 15 | |||
| 16 | const char *test_name = "child-syn-rw"; | ||
| 17 | |||
| 18 | static char buf1[BUF_SIZE]; | ||
| 19 | static char buf2[BUF_SIZE]; | ||
| 20 | |||
| 21 | int | ||
| 22 | main (int argc, const char *argv[]) | ||
| 23 | { | ||
| 24 | int child_idx; | ||
| 25 | int fd; | ||
| 26 | size_t ofs; | ||
| 27 | |||
| 28 | quiet = true; | ||
| 29 | |||
| 30 | CHECK (argc == 2, "argc must be 2, actually %d", argc); | ||
| 31 | child_idx = atoi (argv[1]); | ||
| 32 | |||
| 33 | random_init (0); | ||
| 34 | random_bytes (buf1, sizeof buf1); | ||
| 35 | |||
| 36 | CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); | ||
| 37 | ofs = 0; | ||
| 38 | while (ofs < sizeof buf2) | ||
| 39 | { | ||
| 40 | int bytes_read = read (fd, buf2 + ofs, sizeof buf2 - ofs); | ||
| 41 | CHECK (bytes_read >= -1 && bytes_read <= (int) (sizeof buf2 - ofs), | ||
| 42 | "%zu-byte read on \"%s\" returned invalid value of %d", | ||
| 43 | sizeof buf2 - ofs, file_name, bytes_read); | ||
| 44 | if (bytes_read > 0) | ||
| 45 | { | ||
| 46 | compare_bytes (buf2 + ofs, buf1 + ofs, bytes_read, ofs, file_name); | ||
| 47 | ofs += bytes_read; | ||
| 48 | } | ||
| 49 | } | ||
| 50 | close (fd); | ||
| 51 | |||
| 52 | return child_idx; | ||
| 53 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-empty-name-persistence.ck b/pintos-progos/tests/filesys/extended/dir-empty-name-persistence.ck new file mode 100644 index 0000000..562c451 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-empty-name-persistence.ck | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_archive ({}); | ||
| 6 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-empty-name.c b/pintos-progos/tests/filesys/extended/dir-empty-name.c new file mode 100644 index 0000000..c4859d2 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-empty-name.c | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | /* Tries to create a directory named as the empty string, | ||
| 2 | which must return failure. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | CHECK (!mkdir (""), "mkdir \"\" (must return false)"); | ||
| 12 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-empty-name.ck b/pintos-progos/tests/filesys/extended/dir-empty-name.ck new file mode 100644 index 0000000..d6c5621 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-empty-name.ck | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (dir-empty-name) begin | ||
| 7 | (dir-empty-name) mkdir "" (must return false) | ||
| 8 | (dir-empty-name) end | ||
| 9 | EOF | ||
| 10 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-mk-tree-persistence.ck b/pintos-progos/tests/filesys/extended/dir-mk-tree-persistence.ck new file mode 100644 index 0000000..fb16afd --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-mk-tree-persistence.ck | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | my ($tree); | ||
| 6 | for my $a (0...3) { | ||
| 7 | for my $b (0...2) { | ||
| 8 | for my $c (0...2) { | ||
| 9 | for my $d (0...3) { | ||
| 10 | $tree->{$a}{$b}{$c}{$d} = ['']; | ||
| 11 | } | ||
| 12 | } | ||
| 13 | } | ||
| 14 | } | ||
| 15 | check_archive ($tree); | ||
| 16 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-mk-tree.c b/pintos-progos/tests/filesys/extended/dir-mk-tree.c new file mode 100644 index 0000000..a714ff3 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-mk-tree.c | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | /* Creates directories /0/0/0 through /3/2/2 and creates files in | ||
| 2 | the leaf directories. */ | ||
| 3 | |||
| 4 | #include "tests/filesys/extended/mk-tree.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | make_tree (4, 3, 3, 4); | ||
| 11 | } | ||
| 12 | |||
diff --git a/pintos-progos/tests/filesys/extended/dir-mk-tree.ck b/pintos-progos/tests/filesys/extended/dir-mk-tree.ck new file mode 100644 index 0000000..a8507e2 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-mk-tree.ck | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (dir-mk-tree) begin | ||
| 7 | (dir-mk-tree) creating /0/0/0/0 through /3/2/2/3... | ||
| 8 | (dir-mk-tree) open "/0/2/0/3" | ||
| 9 | (dir-mk-tree) close "/0/2/0/3" | ||
| 10 | (dir-mk-tree) end | ||
| 11 | EOF | ||
| 12 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-mkdir-persistence.ck b/pintos-progos/tests/filesys/extended/dir-mkdir-persistence.ck new file mode 100644 index 0000000..7682900 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-mkdir-persistence.ck | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_archive ({'a' => {'b' => ["\0" x 512]}}); | ||
| 6 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-mkdir.c b/pintos-progos/tests/filesys/extended/dir-mkdir.c new file mode 100644 index 0000000..994f41c --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-mkdir.c | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | /* Tests mkdir(). */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | CHECK (mkdir ("a"), "mkdir \"a\""); | ||
| 11 | CHECK (create ("a/b", 512), "create \"a/b\""); | ||
| 12 | CHECK (chdir ("a"), "chdir \"a\""); | ||
| 13 | CHECK (open ("b") > 1, "open \"b\""); | ||
| 14 | } | ||
| 15 | |||
diff --git a/pintos-progos/tests/filesys/extended/dir-mkdir.ck b/pintos-progos/tests/filesys/extended/dir-mkdir.ck new file mode 100644 index 0000000..4644f80 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-mkdir.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (dir-mkdir) begin | ||
| 7 | (dir-mkdir) mkdir "a" | ||
| 8 | (dir-mkdir) create "a/b" | ||
| 9 | (dir-mkdir) chdir "a" | ||
| 10 | (dir-mkdir) open "b" | ||
| 11 | (dir-mkdir) end | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-open-persistence.ck b/pintos-progos/tests/filesys/extended/dir-open-persistence.ck new file mode 100644 index 0000000..26ff2f1 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-open-persistence.ck | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_archive ({"xyzzy" => {}}); | ||
| 6 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-open.c b/pintos-progos/tests/filesys/extended/dir-open.c new file mode 100644 index 0000000..29d18b8 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-open.c | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | /* Opens a directory, then tries to write to it, which must | ||
| 2 | fail. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | int fd; | ||
| 12 | int retval; | ||
| 13 | |||
| 14 | CHECK (mkdir ("xyzzy"), "mkdir \"xyzzy\""); | ||
| 15 | CHECK ((fd = open ("xyzzy")) > 1, "open \"xyzzy\""); | ||
| 16 | |||
| 17 | msg ("write \"xyzzy\""); | ||
| 18 | retval = write (fd, "foobar", 6); | ||
| 19 | CHECK (retval == -1, | ||
| 20 | "write \"xyzzy\" (must return -1, actually %d)", retval); | ||
| 21 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-open.ck b/pintos-progos/tests/filesys/extended/dir-open.ck new file mode 100644 index 0000000..fccc563 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-open.ck | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (dir-open) begin | ||
| 7 | (dir-open) mkdir "xyzzy" | ||
| 8 | (dir-open) open "xyzzy" | ||
| 9 | (dir-open) write "xyzzy" | ||
| 10 | (dir-open) write "xyzzy" (must return -1, actually -1) | ||
| 11 | (dir-open) end | ||
| 12 | dir-open: exit(0) | ||
| 13 | EOF | ||
| 14 | (dir-open) begin | ||
| 15 | (dir-open) mkdir "xyzzy" | ||
| 16 | (dir-open) open "xyzzy" | ||
| 17 | (dir-open) write "xyzzy" | ||
| 18 | dir-open: exit(-1) | ||
| 19 | EOF | ||
| 20 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-over-file-persistence.ck b/pintos-progos/tests/filesys/extended/dir-over-file-persistence.ck new file mode 100644 index 0000000..56b4ed2 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-over-file-persistence.ck | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_archive ({"abc" => {}}); | ||
| 6 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-over-file.c b/pintos-progos/tests/filesys/extended/dir-over-file.c new file mode 100644 index 0000000..cdd2c62 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-over-file.c | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | /* Tries to create a file with the same name as an existing | ||
| 2 | directory, which must return failure. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | CHECK (mkdir ("abc"), "mkdir \"abc\""); | ||
| 12 | CHECK (!create ("abc", 0), "create \"abc\" (must return false)"); | ||
| 13 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-over-file.ck b/pintos-progos/tests/filesys/extended/dir-over-file.ck new file mode 100644 index 0000000..aae1c1e --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-over-file.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (dir-over-file) begin | ||
| 7 | (dir-over-file) mkdir "abc" | ||
| 8 | (dir-over-file) create "abc" (must return false) | ||
| 9 | (dir-over-file) end | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-cwd-persistence.ck b/pintos-progos/tests/filesys/extended/dir-rm-cwd-persistence.ck new file mode 100644 index 0000000..7533570 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rm-cwd-persistence.ck | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | my ($cwd_removable) = read_text_file ("tests/filesys/extended/can-rmdir-cwd"); | ||
| 6 | $cwd_removable eq 'YES' || $cwd_removable eq 'NO' or die; | ||
| 7 | check_archive ($cwd_removable eq 'YES' ? {} : {"a" => {}}); | ||
| 8 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-cwd.c b/pintos-progos/tests/filesys/extended/dir-rm-cwd.c new file mode 100644 index 0000000..78e13de --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rm-cwd.c | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | /* Tries to remove the current directory, which may succeed or | ||
| 2 | fail. The requirements in each case are different; refer to | ||
| 3 | the assignment for details. */ | ||
| 4 | |||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | static int | ||
| 10 | wrap_open (const char *name) | ||
| 11 | { | ||
| 12 | static int fds[8], fd_cnt; | ||
| 13 | int fd, i; | ||
| 14 | |||
| 15 | CHECK ((fd = open (name)) > 1, "open \"%s\"", name); | ||
| 16 | for (i = 0; i < fd_cnt; i++) | ||
| 17 | if (fds[i] == fd) | ||
| 18 | fail ("fd returned is not unique"); | ||
| 19 | fds[fd_cnt++] = fd; | ||
| 20 | return fd; | ||
| 21 | } | ||
| 22 | |||
| 23 | void | ||
| 24 | test_main (void) | ||
| 25 | { | ||
| 26 | int root_fd, a_fd0; | ||
| 27 | char name[READDIR_MAX_LEN + 1]; | ||
| 28 | |||
| 29 | root_fd = wrap_open ("/"); | ||
| 30 | CHECK (mkdir ("a"), "mkdir \"a\""); | ||
| 31 | |||
| 32 | a_fd0 = wrap_open ("/a"); | ||
| 33 | CHECK (!readdir (a_fd0, name), "verify \"/a\" is empty"); | ||
| 34 | CHECK (inumber (root_fd) != inumber (a_fd0), | ||
| 35 | "\"/\" and \"/a\" must have different inumbers"); | ||
| 36 | |||
| 37 | CHECK (chdir ("a"), "chdir \"a\""); | ||
| 38 | |||
| 39 | msg ("try to remove \"/a\""); | ||
| 40 | if (remove ("/a")) | ||
| 41 | { | ||
| 42 | msg ("remove successful"); | ||
| 43 | |||
| 44 | CHECK (open ("/a") == -1, "open \"/a\" (must fail)"); | ||
| 45 | CHECK (open (".") == -1, "open \".\" (must fail)"); | ||
| 46 | CHECK (open ("..") == -1, "open \"..\" (must fail)"); | ||
| 47 | CHECK (!create ("x", 512), "create \"x\" (must fail)"); | ||
| 48 | } | ||
| 49 | else | ||
| 50 | { | ||
| 51 | int a_fd1, a_fd2, a_fd3; | ||
| 52 | |||
| 53 | msg ("remove failed"); | ||
| 54 | |||
| 55 | CHECK (!remove ("../a"), "try to remove \"../a\" (must fail)"); | ||
| 56 | CHECK (!remove (".././a"), "try to remove \".././a\" (must fail)"); | ||
| 57 | CHECK (!remove ("/./a"), "try to remove \"/./a\" (must fail)"); | ||
| 58 | |||
| 59 | a_fd1 = wrap_open ("/a"); | ||
| 60 | a_fd2 = wrap_open ("."); | ||
| 61 | CHECK (inumber (a_fd1) == inumber (a_fd2), | ||
| 62 | "\"/a\" and \".\" must have same inumber"); | ||
| 63 | CHECK (inumber (root_fd) != inumber (a_fd1), | ||
| 64 | "\"/\" and \"/a\" must have different inumbers"); | ||
| 65 | |||
| 66 | CHECK (chdir ("/a"), "chdir \"/a\""); | ||
| 67 | a_fd3 = wrap_open ("."); | ||
| 68 | CHECK (inumber (a_fd3) == inumber (a_fd1), | ||
| 69 | "\".\" must have same inumber as before"); | ||
| 70 | |||
| 71 | CHECK (chdir ("/"), "chdir \"/\""); | ||
| 72 | CHECK (!remove ("a"), "try to remove \"a\" (must fail: still open)"); | ||
| 73 | } | ||
| 74 | CHECK (!readdir (a_fd0, name), "verify \"/a\" is empty"); | ||
| 75 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-cwd.ck b/pintos-progos/tests/filesys/extended/dir-rm-cwd.ck new file mode 100644 index 0000000..6fa4739 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rm-cwd.ck | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | my ($cwd_removable) = check_expected (IGNORE_EXIT_CODES => 1, | ||
| 6 | {NO => <<'EOF', YES => <<'EOF'}); | ||
| 7 | (dir-rm-cwd) begin | ||
| 8 | (dir-rm-cwd) open "/" | ||
| 9 | (dir-rm-cwd) mkdir "a" | ||
| 10 | (dir-rm-cwd) open "/a" | ||
| 11 | (dir-rm-cwd) verify "/a" is empty | ||
| 12 | (dir-rm-cwd) "/" and "/a" must have different inumbers | ||
| 13 | (dir-rm-cwd) chdir "a" | ||
| 14 | (dir-rm-cwd) try to remove "/a" | ||
| 15 | (dir-rm-cwd) remove failed | ||
| 16 | (dir-rm-cwd) try to remove "../a" (must fail) | ||
| 17 | (dir-rm-cwd) try to remove ".././a" (must fail) | ||
| 18 | (dir-rm-cwd) try to remove "/./a" (must fail) | ||
| 19 | (dir-rm-cwd) open "/a" | ||
| 20 | (dir-rm-cwd) open "." | ||
| 21 | (dir-rm-cwd) "/a" and "." must have same inumber | ||
| 22 | (dir-rm-cwd) "/" and "/a" must have different inumbers | ||
| 23 | (dir-rm-cwd) chdir "/a" | ||
| 24 | (dir-rm-cwd) open "." | ||
| 25 | (dir-rm-cwd) "." must have same inumber as before | ||
| 26 | (dir-rm-cwd) chdir "/" | ||
| 27 | (dir-rm-cwd) try to remove "a" (must fail: still open) | ||
| 28 | (dir-rm-cwd) verify "/a" is empty | ||
| 29 | (dir-rm-cwd) end | ||
| 30 | EOF | ||
| 31 | (dir-rm-cwd) begin | ||
| 32 | (dir-rm-cwd) open "/" | ||
| 33 | (dir-rm-cwd) mkdir "a" | ||
| 34 | (dir-rm-cwd) open "/a" | ||
| 35 | (dir-rm-cwd) verify "/a" is empty | ||
| 36 | (dir-rm-cwd) "/" and "/a" must have different inumbers | ||
| 37 | (dir-rm-cwd) chdir "a" | ||
| 38 | (dir-rm-cwd) try to remove "/a" | ||
| 39 | (dir-rm-cwd) remove successful | ||
| 40 | (dir-rm-cwd) open "/a" (must fail) | ||
| 41 | (dir-rm-cwd) open "." (must fail) | ||
| 42 | (dir-rm-cwd) open ".." (must fail) | ||
| 43 | (dir-rm-cwd) create "x" (must fail) | ||
| 44 | (dir-rm-cwd) verify "/a" is empty | ||
| 45 | (dir-rm-cwd) end | ||
| 46 | EOF | ||
| 47 | open (CAN_RMDIR_CWD, ">tests/filesys/extended/can-rmdir-cwd") | ||
| 48 | or die "tests/filesys/extended/can-rmdir-cwd: create: $!\n"; | ||
| 49 | print CAN_RMDIR_CWD "$cwd_removable"; | ||
| 50 | close (CAN_RMDIR_CWD); | ||
| 51 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-parent-persistence.ck b/pintos-progos/tests/filesys/extended/dir-rm-parent-persistence.ck new file mode 100644 index 0000000..f30b04a --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rm-parent-persistence.ck | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_archive ({"a" => {"b" => {}}}); | ||
| 6 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-parent.c b/pintos-progos/tests/filesys/extended/dir-rm-parent.c new file mode 100644 index 0000000..eb43f5b --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rm-parent.c | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | /* Tries to remove a parent of the current directory. This must | ||
| 2 | fail, because that directory is non-empty. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | CHECK (mkdir ("a"), "mkdir \"a\""); | ||
| 12 | CHECK (chdir ("a"), "chdir \"a\""); | ||
| 13 | CHECK (mkdir ("b"), "mkdir \"b\""); | ||
| 14 | CHECK (chdir ("b"), "chdir \"b\""); | ||
| 15 | CHECK (!remove ("/a"), "remove \"/a\" (must fail)"); | ||
| 16 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-parent.ck b/pintos-progos/tests/filesys/extended/dir-rm-parent.ck new file mode 100644 index 0000000..9fea8f2 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rm-parent.ck | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (dir-rm-parent) begin | ||
| 7 | (dir-rm-parent) mkdir "a" | ||
| 8 | (dir-rm-parent) chdir "a" | ||
| 9 | (dir-rm-parent) mkdir "b" | ||
| 10 | (dir-rm-parent) chdir "b" | ||
| 11 | (dir-rm-parent) remove "/a" (must fail) | ||
| 12 | (dir-rm-parent) end | ||
| 13 | EOF | ||
| 14 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-root-persistence.ck b/pintos-progos/tests/filesys/extended/dir-rm-root-persistence.ck new file mode 100644 index 0000000..6315107 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rm-root-persistence.ck | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_archive ({"a" => ["\0" x 243]}); | ||
| 6 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-root.c b/pintos-progos/tests/filesys/extended/dir-rm-root.c new file mode 100644 index 0000000..c47f1eb --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rm-root.c | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | /* Try to remove the root directory. | ||
| 2 | This must fail. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | CHECK (!remove ("/"), "remove \"/\" (must fail)"); | ||
| 12 | CHECK (create ("/a", 243), "create \"/a\""); | ||
| 13 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-root.ck b/pintos-progos/tests/filesys/extended/dir-rm-root.ck new file mode 100644 index 0000000..8a69ff3 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rm-root.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (dir-rm-root) begin | ||
| 7 | (dir-rm-root) remove "/" (must fail) | ||
| 8 | (dir-rm-root) create "/a" | ||
| 9 | (dir-rm-root) end | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-tree-persistence.ck b/pintos-progos/tests/filesys/extended/dir-rm-tree-persistence.ck new file mode 100644 index 0000000..562c451 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rm-tree-persistence.ck | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_archive ({}); | ||
| 6 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-tree.c b/pintos-progos/tests/filesys/extended/dir-rm-tree.c new file mode 100644 index 0000000..bab41a6 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rm-tree.c | |||
| @@ -0,0 +1,62 @@ | |||
| 1 | /* Creates directories /0/0/0 through /3/2/2 and files in the | ||
| 2 | leaf directories, then removes them. */ | ||
| 3 | |||
| 4 | #include <stdarg.h> | ||
| 5 | #include <stdio.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | #include "tests/filesys/extended/mk-tree.h" | ||
| 8 | #include "tests/lib.h" | ||
| 9 | #include "tests/main.h" | ||
| 10 | |||
| 11 | static void remove_tree (int at, int bt, int ct, int dt); | ||
| 12 | |||
| 13 | void | ||
| 14 | test_main (void) | ||
| 15 | { | ||
| 16 | make_tree (4, 3, 3, 4); | ||
| 17 | remove_tree (4, 3, 3, 4); | ||
| 18 | } | ||
| 19 | |||
| 20 | static void do_remove (const char *format, ...) PRINTF_FORMAT (1, 2); | ||
| 21 | |||
| 22 | static void | ||
| 23 | remove_tree (int at, int bt, int ct, int dt) | ||
| 24 | { | ||
| 25 | char try[128]; | ||
| 26 | int a, b, c, d; | ||
| 27 | |||
| 28 | msg ("removing /0/0/0/0 through /%d/%d/%d/%d...", | ||
| 29 | at - 1, bt - 1, ct - 1, dt - 1); | ||
| 30 | quiet = true; | ||
| 31 | for (a = 0; a < at; a++) | ||
| 32 | { | ||
| 33 | for (b = 0; b < bt; b++) | ||
| 34 | { | ||
| 35 | for (c = 0; c < ct; c++) | ||
| 36 | { | ||
| 37 | for (d = 0; d < dt; d++) | ||
| 38 | do_remove ("/%d/%d/%d/%d", a, b, c, d); | ||
| 39 | do_remove ("/%d/%d/%d", a, b, c); | ||
| 40 | } | ||
| 41 | do_remove ("/%d/%d", a, b); | ||
| 42 | } | ||
| 43 | do_remove ("/%d", a); | ||
| 44 | } | ||
| 45 | quiet = false; | ||
| 46 | |||
| 47 | snprintf (try, sizeof (try), "/%d/%d/%d/%d", at - 1, 0, ct - 1, 0); | ||
| 48 | CHECK (open (try) == -1, "open \"%s\" (must return -1)", try); | ||
| 49 | } | ||
| 50 | |||
| 51 | static void | ||
| 52 | do_remove (const char *format, ...) | ||
| 53 | { | ||
| 54 | char name[128]; | ||
| 55 | va_list args; | ||
| 56 | |||
| 57 | va_start (args, format); | ||
| 58 | vsnprintf (name, sizeof name, format, args); | ||
| 59 | va_end (args); | ||
| 60 | |||
| 61 | CHECK (remove (name), "remove \"%s\"", name); | ||
| 62 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-tree.ck b/pintos-progos/tests/filesys/extended/dir-rm-tree.ck new file mode 100644 index 0000000..587b493 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rm-tree.ck | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (dir-rm-tree) begin | ||
| 7 | (dir-rm-tree) creating /0/0/0/0 through /3/2/2/3... | ||
| 8 | (dir-rm-tree) open "/0/2/0/3" | ||
| 9 | (dir-rm-tree) close "/0/2/0/3" | ||
| 10 | (dir-rm-tree) removing /0/0/0/0 through /3/2/2/3... | ||
| 11 | (dir-rm-tree) open "/3/0/2/0" (must return -1) | ||
| 12 | (dir-rm-tree) end | ||
| 13 | EOF | ||
| 14 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rmdir-persistence.ck b/pintos-progos/tests/filesys/extended/dir-rmdir-persistence.ck new file mode 100644 index 0000000..562c451 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rmdir-persistence.ck | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_archive ({}); | ||
| 6 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rmdir.c b/pintos-progos/tests/filesys/extended/dir-rmdir.c new file mode 100644 index 0000000..b3cbc6f --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rmdir.c | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | /* Creates and removes a directory, then makes sure that it's | ||
| 2 | really gone. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | CHECK (mkdir ("a"), "mkdir \"a\""); | ||
| 12 | CHECK (remove ("a"), "rmdir \"a\""); | ||
| 13 | CHECK (!chdir ("a"), "chdir \"a\" (must return false)"); | ||
| 14 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rmdir.ck b/pintos-progos/tests/filesys/extended/dir-rmdir.ck new file mode 100644 index 0000000..e0d8922 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rmdir.ck | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (dir-rmdir) begin | ||
| 7 | (dir-rmdir) mkdir "a" | ||
| 8 | (dir-rmdir) rmdir "a" | ||
| 9 | (dir-rmdir) chdir "a" (must return false) | ||
| 10 | (dir-rmdir) end | ||
| 11 | EOF | ||
| 12 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-under-file-persistence.ck b/pintos-progos/tests/filesys/extended/dir-under-file-persistence.ck new file mode 100644 index 0000000..67ca528 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-under-file-persistence.ck | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_archive ({"abc" => ['']}); | ||
| 6 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-under-file.c b/pintos-progos/tests/filesys/extended/dir-under-file.c new file mode 100644 index 0000000..973a8b1 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-under-file.c | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | /* Tries to create a directory with the same name as an existing | ||
| 2 | file, which must return failure. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | CHECK (create ("abc", 0), "create \"abc\""); | ||
| 12 | CHECK (!mkdir ("abc"), "mkdir \"abc\" (must return false)"); | ||
| 13 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-under-file.ck b/pintos-progos/tests/filesys/extended/dir-under-file.ck new file mode 100644 index 0000000..cce23b4 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-under-file.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (dir-under-file) begin | ||
| 7 | (dir-under-file) create "abc" | ||
| 8 | (dir-under-file) mkdir "abc" (must return false) | ||
| 9 | (dir-under-file) end | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-vine-persistence.ck b/pintos-progos/tests/filesys/extended/dir-vine-persistence.ck new file mode 100644 index 0000000..698ef01 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-vine-persistence.ck | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | # The archive should look like this: | ||
| 6 | # | ||
| 7 | # 40642 dir-vine | ||
| 8 | # 42479 tar | ||
| 9 | # 0 start | ||
| 10 | # 11 start/file0 | ||
| 11 | # 0 start/dir0 | ||
| 12 | # 11 start/dir0/file1 | ||
| 13 | # 0 start/dir0/dir1 | ||
| 14 | # 11 start/dir0/dir1/file2 | ||
| 15 | # 0 start/dir0/dir1/dir2 | ||
| 16 | # 11 start/dir0/dir1/dir2/file3 | ||
| 17 | # 0 start/dir0/dir1/dir2/dir3 | ||
| 18 | # 11 start/dir0/dir1/dir2/dir3/file4 | ||
| 19 | # 0 start/dir0/dir1/dir2/dir3/dir4 | ||
| 20 | # 11 start/dir0/dir1/dir2/dir3/dir4/file5 | ||
| 21 | # 0 start/dir0/dir1/dir2/dir3/dir4/dir5 | ||
| 22 | # 11 start/dir0/dir1/dir2/dir3/dir4/dir5/file6 | ||
| 23 | # 0 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6 | ||
| 24 | # 11 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/file7 | ||
| 25 | # 0 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7 | ||
| 26 | # 11 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/file8 | ||
| 27 | # 0 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8 | ||
| 28 | # 11 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/file9 | ||
| 29 | # 0 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9 | ||
| 30 | my ($dir) = {}; | ||
| 31 | my ($root) = {"start" => $dir}; | ||
| 32 | for (my ($i) = 0; $i < 10; $i++) { | ||
| 33 | $dir->{"file$i"} = ["contents $i\n"]; | ||
| 34 | $dir = $dir->{"dir$i"} = {}; | ||
| 35 | } | ||
| 36 | check_archive ($root); | ||
| 37 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-vine.c b/pintos-progos/tests/filesys/extended/dir-vine.c new file mode 100644 index 0000000..8a31c38 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-vine.c | |||
| @@ -0,0 +1,85 @@ | |||
| 1 | /* Create a very deep "vine" of directories: /dir0/dir1/dir2/... | ||
| 2 | and an ordinary file in each of them, until we fill up the | ||
| 3 | disk. | ||
| 4 | |||
| 5 | Then delete most of them, for two reasons. First, "tar" | ||
| 6 | limits file names to 100 characters (which could be extended | ||
| 7 | to 256 without much trouble). Second, a full disk has no room | ||
| 8 | for the tar archive. */ | ||
| 9 | |||
| 10 | #include <string.h> | ||
| 11 | #include <stdio.h> | ||
| 12 | #include <syscall.h> | ||
| 13 | #include "tests/lib.h" | ||
| 14 | #include "tests/main.h" | ||
| 15 | |||
| 16 | void | ||
| 17 | test_main (void) | ||
| 18 | { | ||
| 19 | int i; | ||
| 20 | |||
| 21 | msg ("creating many levels of files and directories..."); | ||
| 22 | quiet = true; | ||
| 23 | CHECK (mkdir ("start"), "mkdir \"start\""); | ||
| 24 | CHECK (chdir ("start"), "chdir \"start\""); | ||
| 25 | for (i = 0; ; i++) | ||
| 26 | { | ||
| 27 | char name[3][READDIR_MAX_LEN + 1]; | ||
| 28 | char file_name[16], dir_name[16]; | ||
| 29 | char contents[128]; | ||
| 30 | int fd; | ||
| 31 | |||
| 32 | /* Create file. */ | ||
| 33 | snprintf (file_name, sizeof file_name, "file%d", i); | ||
| 34 | if (!create (file_name, 0)) | ||
| 35 | break; | ||
| 36 | CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); | ||
| 37 | snprintf (contents, sizeof contents, "contents %d\n", i); | ||
| 38 | if (write (fd, contents, strlen (contents)) != (int) strlen (contents)) | ||
| 39 | { | ||
| 40 | CHECK (remove (file_name), "remove \"%s\"", file_name); | ||
| 41 | close (fd); | ||
| 42 | break; | ||
| 43 | } | ||
| 44 | close (fd); | ||
| 45 | |||
| 46 | /* Create directory. */ | ||
| 47 | snprintf (dir_name, sizeof dir_name, "dir%d", i); | ||
| 48 | if (!mkdir (dir_name)) | ||
| 49 | { | ||
| 50 | CHECK (remove (file_name), "remove \"%s\"", file_name); | ||
| 51 | break; | ||
| 52 | } | ||
| 53 | |||
| 54 | /* Check for file and directory. */ | ||
| 55 | CHECK ((fd = open (".")) > 1, "open \".\""); | ||
| 56 | CHECK (readdir (fd, name[0]), "readdir \".\""); | ||
| 57 | CHECK (readdir (fd, name[1]), "readdir \".\""); | ||
| 58 | CHECK (!readdir (fd, name[2]), "readdir \".\" (should fail)"); | ||
| 59 | CHECK ((!strcmp (name[0], dir_name) && !strcmp (name[1], file_name)) | ||
| 60 | || (!strcmp (name[1], dir_name) && !strcmp (name[0], file_name)), | ||
| 61 | "names should be \"%s\" and \"%s\", " | ||
| 62 | "actually \"%s\" and \"%s\"", | ||
| 63 | file_name, dir_name, name[0], name[1]); | ||
| 64 | close (fd); | ||
| 65 | |||
| 66 | /* Descend into directory. */ | ||
| 67 | CHECK (chdir (dir_name), "chdir \"%s\"", dir_name); | ||
| 68 | } | ||
| 69 | CHECK (i > 200, "created files and directories only to level %d", i); | ||
| 70 | quiet = false; | ||
| 71 | |||
| 72 | msg ("removing all but top 10 levels of files and directories..."); | ||
| 73 | quiet = true; | ||
| 74 | while (i-- > 10) | ||
| 75 | { | ||
| 76 | char file_name[16], dir_name[16]; | ||
| 77 | |||
| 78 | snprintf (file_name, sizeof file_name, "file%d", i); | ||
| 79 | snprintf (dir_name, sizeof dir_name, "dir%d", i); | ||
| 80 | CHECK (chdir (".."), "chdir \"..\""); | ||
| 81 | CHECK (remove (dir_name), "remove \"%s\"", dir_name); | ||
| 82 | CHECK (remove (file_name), "remove \"%s\"", file_name); | ||
| 83 | } | ||
| 84 | quiet = false; | ||
| 85 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-vine.ck b/pintos-progos/tests/filesys/extended/dir-vine.ck new file mode 100644 index 0000000..db452b0 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-vine.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (dir-vine) begin | ||
| 7 | (dir-vine) creating many levels of files and directories... | ||
| 8 | (dir-vine) removing all but top 10 levels of files and directories... | ||
| 9 | (dir-vine) end | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-create-persistence.ck b/pintos-progos/tests/filesys/extended/grow-create-persistence.ck new file mode 100644 index 0000000..bbcb24f --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-create-persistence.ck | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_archive ({"blargle" => ['']}); | ||
| 6 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-create.c b/pintos-progos/tests/filesys/extended/grow-create.c new file mode 100644 index 0000000..9ccc4ea --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-create.c | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | /* Create a file of size 0. */ | ||
| 2 | |||
| 3 | #define TEST_SIZE 0 | ||
| 4 | #include "tests/filesys/create.inc" | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-create.ck b/pintos-progos/tests/filesys/extended/grow-create.ck new file mode 100644 index 0000000..b2e69d1 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-create.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (grow-create) begin | ||
| 7 | (grow-create) create "blargle" | ||
| 8 | (grow-create) open "blargle" for verification | ||
| 9 | (grow-create) verified contents of "blargle" | ||
| 10 | (grow-create) close "blargle" | ||
| 11 | (grow-create) end | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-dir-lg-persistence.ck b/pintos-progos/tests/filesys/extended/grow-dir-lg-persistence.ck new file mode 100644 index 0000000..989a322 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-dir-lg-persistence.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | my ($fs); | ||
| 7 | $fs->{'x'}{"file$_"} = [random_bytes (512)] foreach 0...49; | ||
| 8 | check_archive ($fs); | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-dir-lg.c b/pintos-progos/tests/filesys/extended/grow-dir-lg.c new file mode 100644 index 0000000..20a194b --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-dir-lg.c | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | /* Creates a directory, | ||
| 2 | then creates 50 files in that directory. */ | ||
| 3 | |||
| 4 | #define FILE_CNT 50 | ||
| 5 | #define DIRECTORY "/x" | ||
| 6 | #include "tests/filesys/extended/grow-dir.inc" | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-dir-lg.ck b/pintos-progos/tests/filesys/extended/grow-dir-lg.ck new file mode 100644 index 0000000..ec58bd3 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-dir-lg.ck | |||
| @@ -0,0 +1,61 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 7 | (grow-dir-lg) begin | ||
| 8 | (grow-dir-lg) mkdir /x | ||
| 9 | (grow-dir-lg) creating and checking "/x/file0" | ||
| 10 | (grow-dir-lg) creating and checking "/x/file1" | ||
| 11 | (grow-dir-lg) creating and checking "/x/file2" | ||
| 12 | (grow-dir-lg) creating and checking "/x/file3" | ||
| 13 | (grow-dir-lg) creating and checking "/x/file4" | ||
| 14 | (grow-dir-lg) creating and checking "/x/file5" | ||
| 15 | (grow-dir-lg) creating and checking "/x/file6" | ||
| 16 | (grow-dir-lg) creating and checking "/x/file7" | ||
| 17 | (grow-dir-lg) creating and checking "/x/file8" | ||
| 18 | (grow-dir-lg) creating and checking "/x/file9" | ||
| 19 | (grow-dir-lg) creating and checking "/x/file10" | ||
| 20 | (grow-dir-lg) creating and checking "/x/file11" | ||
| 21 | (grow-dir-lg) creating and checking "/x/file12" | ||
| 22 | (grow-dir-lg) creating and checking "/x/file13" | ||
| 23 | (grow-dir-lg) creating and checking "/x/file14" | ||
| 24 | (grow-dir-lg) creating and checking "/x/file15" | ||
| 25 | (grow-dir-lg) creating and checking "/x/file16" | ||
| 26 | (grow-dir-lg) creating and checking "/x/file17" | ||
| 27 | (grow-dir-lg) creating and checking "/x/file18" | ||
| 28 | (grow-dir-lg) creating and checking "/x/file19" | ||
| 29 | (grow-dir-lg) creating and checking "/x/file20" | ||
| 30 | (grow-dir-lg) creating and checking "/x/file21" | ||
| 31 | (grow-dir-lg) creating and checking "/x/file22" | ||
| 32 | (grow-dir-lg) creating and checking "/x/file23" | ||
| 33 | (grow-dir-lg) creating and checking "/x/file24" | ||
| 34 | (grow-dir-lg) creating and checking "/x/file25" | ||
| 35 | (grow-dir-lg) creating and checking "/x/file26" | ||
| 36 | (grow-dir-lg) creating and checking "/x/file27" | ||
| 37 | (grow-dir-lg) creating and checking "/x/file28" | ||
| 38 | (grow-dir-lg) creating and checking "/x/file29" | ||
| 39 | (grow-dir-lg) creating and checking "/x/file30" | ||
| 40 | (grow-dir-lg) creating and checking "/x/file31" | ||
| 41 | (grow-dir-lg) creating and checking "/x/file32" | ||
| 42 | (grow-dir-lg) creating and checking "/x/file33" | ||
| 43 | (grow-dir-lg) creating and checking "/x/file34" | ||
| 44 | (grow-dir-lg) creating and checking "/x/file35" | ||
| 45 | (grow-dir-lg) creating and checking "/x/file36" | ||
| 46 | (grow-dir-lg) creating and checking "/x/file37" | ||
| 47 | (grow-dir-lg) creating and checking "/x/file38" | ||
| 48 | (grow-dir-lg) creating and checking "/x/file39" | ||
| 49 | (grow-dir-lg) creating and checking "/x/file40" | ||
| 50 | (grow-dir-lg) creating and checking "/x/file41" | ||
| 51 | (grow-dir-lg) creating and checking "/x/file42" | ||
| 52 | (grow-dir-lg) creating and checking "/x/file43" | ||
| 53 | (grow-dir-lg) creating and checking "/x/file44" | ||
| 54 | (grow-dir-lg) creating and checking "/x/file45" | ||
| 55 | (grow-dir-lg) creating and checking "/x/file46" | ||
| 56 | (grow-dir-lg) creating and checking "/x/file47" | ||
| 57 | (grow-dir-lg) creating and checking "/x/file48" | ||
| 58 | (grow-dir-lg) creating and checking "/x/file49" | ||
| 59 | (grow-dir-lg) end | ||
| 60 | EOF | ||
| 61 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-dir.inc b/pintos-progos/tests/filesys/extended/grow-dir.inc new file mode 100644 index 0000000..bee0ba0 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-dir.inc | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | /* -*- c -*- */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include <stdio.h> | ||
| 5 | #include "tests/filesys/seq-test.h" | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | static char buf[512]; | ||
| 10 | |||
| 11 | static size_t | ||
| 12 | return_block_size (void) | ||
| 13 | { | ||
| 14 | return sizeof buf; | ||
| 15 | } | ||
| 16 | |||
| 17 | void | ||
| 18 | test_main (void) | ||
| 19 | { | ||
| 20 | size_t i; | ||
| 21 | |||
| 22 | #ifdef DIRECTORY | ||
| 23 | CHECK (mkdir (DIRECTORY), "mkdir %s", DIRECTORY); | ||
| 24 | #define DIR_PREFIX DIRECTORY "/" | ||
| 25 | #else | ||
| 26 | #define DIR_PREFIX "" | ||
| 27 | #endif | ||
| 28 | for (i = 0; i < FILE_CNT; i++) | ||
| 29 | { | ||
| 30 | char file_name[128]; | ||
| 31 | snprintf (file_name, sizeof file_name, "%sfile%zu", DIR_PREFIX, i); | ||
| 32 | |||
| 33 | msg ("creating and checking \"%s\"", file_name); | ||
| 34 | |||
| 35 | quiet = true; | ||
| 36 | seq_test (file_name, | ||
| 37 | buf, sizeof buf, sizeof buf, | ||
| 38 | return_block_size, NULL); | ||
| 39 | quiet = false; | ||
| 40 | } | ||
| 41 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-file-size-persistence.ck b/pintos-progos/tests/filesys/extended/grow-file-size-persistence.ck new file mode 100644 index 0000000..150f383 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-file-size-persistence.ck | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | check_archive ({"testfile" => [random_bytes (2134)]}); | ||
| 7 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-file-size.c b/pintos-progos/tests/filesys/extended/grow-file-size.c new file mode 100644 index 0000000..3ce8588 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-file-size.c | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | /* Grows a file from 0 bytes to 2,134 bytes, 37 bytes at a time, | ||
| 2 | and checks that the file's size is reported correctly at each | ||
| 3 | step. */ | ||
| 4 | |||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/filesys/seq-test.h" | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | static char buf[2134]; | ||
| 11 | |||
| 12 | static size_t | ||
| 13 | return_block_size (void) | ||
| 14 | { | ||
| 15 | return 37; | ||
| 16 | } | ||
| 17 | |||
| 18 | static void | ||
| 19 | check_file_size (int fd, long ofs) | ||
| 20 | { | ||
| 21 | long size = filesize (fd); | ||
| 22 | if (size != ofs) | ||
| 23 | fail ("filesize not updated properly: should be %ld, actually %ld", | ||
| 24 | ofs, size); | ||
| 25 | } | ||
| 26 | |||
| 27 | void | ||
| 28 | test_main (void) | ||
| 29 | { | ||
| 30 | seq_test ("testfile", | ||
| 31 | buf, sizeof buf, 0, | ||
| 32 | return_block_size, check_file_size); | ||
| 33 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-file-size.ck b/pintos-progos/tests/filesys/extended/grow-file-size.ck new file mode 100644 index 0000000..d81feff --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-file-size.ck | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 7 | (grow-file-size) begin | ||
| 8 | (grow-file-size) create "testfile" | ||
| 9 | (grow-file-size) open "testfile" | ||
| 10 | (grow-file-size) writing "testfile" | ||
| 11 | (grow-file-size) close "testfile" | ||
| 12 | (grow-file-size) open "testfile" for verification | ||
| 13 | (grow-file-size) verified contents of "testfile" | ||
| 14 | (grow-file-size) close "testfile" | ||
| 15 | (grow-file-size) end | ||
| 16 | EOF | ||
| 17 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-root-lg-persistence.ck b/pintos-progos/tests/filesys/extended/grow-root-lg-persistence.ck new file mode 100644 index 0000000..1692f46 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-root-lg-persistence.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | my ($fs); | ||
| 7 | $fs->{"file$_"} = [random_bytes (512)] foreach 0...49; | ||
| 8 | check_archive ($fs); | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-root-lg.c b/pintos-progos/tests/filesys/extended/grow-root-lg.c new file mode 100644 index 0000000..d8d6c09 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-root-lg.c | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | /* Creates 50 files in the root directory. */ | ||
| 2 | |||
| 3 | #define FILE_CNT 50 | ||
| 4 | #include "tests/filesys/extended/grow-dir.inc" | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-root-lg.ck b/pintos-progos/tests/filesys/extended/grow-root-lg.ck new file mode 100644 index 0000000..b174bc9 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-root-lg.ck | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 7 | (grow-root-lg) begin | ||
| 8 | (grow-root-lg) creating and checking "file0" | ||
| 9 | (grow-root-lg) creating and checking "file1" | ||
| 10 | (grow-root-lg) creating and checking "file2" | ||
| 11 | (grow-root-lg) creating and checking "file3" | ||
| 12 | (grow-root-lg) creating and checking "file4" | ||
| 13 | (grow-root-lg) creating and checking "file5" | ||
| 14 | (grow-root-lg) creating and checking "file6" | ||
| 15 | (grow-root-lg) creating and checking "file7" | ||
| 16 | (grow-root-lg) creating and checking "file8" | ||
| 17 | (grow-root-lg) creating and checking "file9" | ||
| 18 | (grow-root-lg) creating and checking "file10" | ||
| 19 | (grow-root-lg) creating and checking "file11" | ||
| 20 | (grow-root-lg) creating and checking "file12" | ||
| 21 | (grow-root-lg) creating and checking "file13" | ||
| 22 | (grow-root-lg) creating and checking "file14" | ||
| 23 | (grow-root-lg) creating and checking "file15" | ||
| 24 | (grow-root-lg) creating and checking "file16" | ||
| 25 | (grow-root-lg) creating and checking "file17" | ||
| 26 | (grow-root-lg) creating and checking "file18" | ||
| 27 | (grow-root-lg) creating and checking "file19" | ||
| 28 | (grow-root-lg) creating and checking "file20" | ||
| 29 | (grow-root-lg) creating and checking "file21" | ||
| 30 | (grow-root-lg) creating and checking "file22" | ||
| 31 | (grow-root-lg) creating and checking "file23" | ||
| 32 | (grow-root-lg) creating and checking "file24" | ||
| 33 | (grow-root-lg) creating and checking "file25" | ||
| 34 | (grow-root-lg) creating and checking "file26" | ||
| 35 | (grow-root-lg) creating and checking "file27" | ||
| 36 | (grow-root-lg) creating and checking "file28" | ||
| 37 | (grow-root-lg) creating and checking "file29" | ||
| 38 | (grow-root-lg) creating and checking "file30" | ||
| 39 | (grow-root-lg) creating and checking "file31" | ||
| 40 | (grow-root-lg) creating and checking "file32" | ||
| 41 | (grow-root-lg) creating and checking "file33" | ||
| 42 | (grow-root-lg) creating and checking "file34" | ||
| 43 | (grow-root-lg) creating and checking "file35" | ||
| 44 | (grow-root-lg) creating and checking "file36" | ||
| 45 | (grow-root-lg) creating and checking "file37" | ||
| 46 | (grow-root-lg) creating and checking "file38" | ||
| 47 | (grow-root-lg) creating and checking "file39" | ||
| 48 | (grow-root-lg) creating and checking "file40" | ||
| 49 | (grow-root-lg) creating and checking "file41" | ||
| 50 | (grow-root-lg) creating and checking "file42" | ||
| 51 | (grow-root-lg) creating and checking "file43" | ||
| 52 | (grow-root-lg) creating and checking "file44" | ||
| 53 | (grow-root-lg) creating and checking "file45" | ||
| 54 | (grow-root-lg) creating and checking "file46" | ||
| 55 | (grow-root-lg) creating and checking "file47" | ||
| 56 | (grow-root-lg) creating and checking "file48" | ||
| 57 | (grow-root-lg) creating and checking "file49" | ||
| 58 | (grow-root-lg) end | ||
| 59 | EOF | ||
| 60 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-root-sm-persistence.ck b/pintos-progos/tests/filesys/extended/grow-root-sm-persistence.ck new file mode 100644 index 0000000..2b0b8ab --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-root-sm-persistence.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | my ($fs); | ||
| 7 | $fs->{"file$_"} = [random_bytes (512)] foreach 0...19; | ||
| 8 | check_archive ($fs); | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-root-sm.c b/pintos-progos/tests/filesys/extended/grow-root-sm.c new file mode 100644 index 0000000..ee375d5 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-root-sm.c | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | /* Creates 20 files in the root directory. */ | ||
| 2 | |||
| 3 | #define FILE_CNT 20 | ||
| 4 | #include "tests/filesys/extended/grow-dir.inc" | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-root-sm.ck b/pintos-progos/tests/filesys/extended/grow-root-sm.ck new file mode 100644 index 0000000..1aac7e9 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-root-sm.ck | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 7 | (grow-root-sm) begin | ||
| 8 | (grow-root-sm) creating and checking "file0" | ||
| 9 | (grow-root-sm) creating and checking "file1" | ||
| 10 | (grow-root-sm) creating and checking "file2" | ||
| 11 | (grow-root-sm) creating and checking "file3" | ||
| 12 | (grow-root-sm) creating and checking "file4" | ||
| 13 | (grow-root-sm) creating and checking "file5" | ||
| 14 | (grow-root-sm) creating and checking "file6" | ||
| 15 | (grow-root-sm) creating and checking "file7" | ||
| 16 | (grow-root-sm) creating and checking "file8" | ||
| 17 | (grow-root-sm) creating and checking "file9" | ||
| 18 | (grow-root-sm) creating and checking "file10" | ||
| 19 | (grow-root-sm) creating and checking "file11" | ||
| 20 | (grow-root-sm) creating and checking "file12" | ||
| 21 | (grow-root-sm) creating and checking "file13" | ||
| 22 | (grow-root-sm) creating and checking "file14" | ||
| 23 | (grow-root-sm) creating and checking "file15" | ||
| 24 | (grow-root-sm) creating and checking "file16" | ||
| 25 | (grow-root-sm) creating and checking "file17" | ||
| 26 | (grow-root-sm) creating and checking "file18" | ||
| 27 | (grow-root-sm) creating and checking "file19" | ||
| 28 | (grow-root-sm) end | ||
| 29 | EOF | ||
| 30 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-seq-lg-persistence.ck b/pintos-progos/tests/filesys/extended/grow-seq-lg-persistence.ck new file mode 100644 index 0000000..41aaae0 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-seq-lg-persistence.ck | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | check_archive ({"testme" => [random_bytes (72943)]}); | ||
| 7 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-seq-lg.c b/pintos-progos/tests/filesys/extended/grow-seq-lg.c new file mode 100644 index 0000000..3108d17 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-seq-lg.c | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | /* Grows a file from 0 bytes to 72,943 bytes, 1,234 bytes at a | ||
| 2 | time. */ | ||
| 3 | |||
| 4 | #define TEST_SIZE 72943 | ||
| 5 | #include "tests/filesys/extended/grow-seq.inc" | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-seq-lg.ck b/pintos-progos/tests/filesys/extended/grow-seq-lg.ck new file mode 100644 index 0000000..90fcd8c --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-seq-lg.ck | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 7 | (grow-seq-lg) begin | ||
| 8 | (grow-seq-lg) create "testme" | ||
| 9 | (grow-seq-lg) open "testme" | ||
| 10 | (grow-seq-lg) writing "testme" | ||
| 11 | (grow-seq-lg) close "testme" | ||
| 12 | (grow-seq-lg) open "testme" for verification | ||
| 13 | (grow-seq-lg) verified contents of "testme" | ||
| 14 | (grow-seq-lg) close "testme" | ||
| 15 | (grow-seq-lg) end | ||
| 16 | EOF | ||
| 17 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-seq-sm-persistence.ck b/pintos-progos/tests/filesys/extended/grow-seq-sm-persistence.ck new file mode 100644 index 0000000..6cb0bd8 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-seq-sm-persistence.ck | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | check_archive ({"testme" => [random_bytes (5678)]}); | ||
| 7 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-seq-sm.c b/pintos-progos/tests/filesys/extended/grow-seq-sm.c new file mode 100644 index 0000000..3656e2e --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-seq-sm.c | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | /* Grows a file from 0 bytes to 5,678 bytes, 1,234 bytes at a | ||
| 2 | time. */ | ||
| 3 | |||
| 4 | #define TEST_SIZE 5678 | ||
| 5 | #include "tests/filesys/extended/grow-seq.inc" | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-seq-sm.ck b/pintos-progos/tests/filesys/extended/grow-seq-sm.ck new file mode 100644 index 0000000..5cf4518 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-seq-sm.ck | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 7 | (grow-seq-sm) begin | ||
| 8 | (grow-seq-sm) create "testme" | ||
| 9 | (grow-seq-sm) open "testme" | ||
| 10 | (grow-seq-sm) writing "testme" | ||
| 11 | (grow-seq-sm) close "testme" | ||
| 12 | (grow-seq-sm) open "testme" for verification | ||
| 13 | (grow-seq-sm) verified contents of "testme" | ||
| 14 | (grow-seq-sm) close "testme" | ||
| 15 | (grow-seq-sm) end | ||
| 16 | EOF | ||
| 17 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-seq.inc b/pintos-progos/tests/filesys/extended/grow-seq.inc new file mode 100644 index 0000000..1b7710c --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-seq.inc | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | /* -*- c -*- */ | ||
| 2 | |||
| 3 | #include "tests/filesys/seq-test.h" | ||
| 4 | #include "tests/main.h" | ||
| 5 | |||
| 6 | static char buf[TEST_SIZE]; | ||
| 7 | |||
| 8 | static size_t | ||
| 9 | return_block_size (void) | ||
| 10 | { | ||
| 11 | return 1234; | ||
| 12 | } | ||
| 13 | |||
| 14 | void | ||
| 15 | test_main (void) | ||
| 16 | { | ||
| 17 | seq_test ("testme", | ||
| 18 | buf, sizeof buf, 0, | ||
| 19 | return_block_size, NULL); | ||
| 20 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-sparse-persistence.ck b/pintos-progos/tests/filesys/extended/grow-sparse-persistence.ck new file mode 100644 index 0000000..3f06a5b --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-sparse-persistence.ck | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_archive ({"testfile" => ["\0" x 76543]}); | ||
| 6 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-sparse.c b/pintos-progos/tests/filesys/extended/grow-sparse.c new file mode 100644 index 0000000..6eab210 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-sparse.c | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | /* Tests that seeking past the end of a file and writing will | ||
| 2 | properly zero out the region in between. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | static char buf[76543]; | ||
| 9 | |||
| 10 | void | ||
| 11 | test_main (void) | ||
| 12 | { | ||
| 13 | const char *file_name = "testfile"; | ||
| 14 | char zero = 0; | ||
| 15 | int fd; | ||
| 16 | |||
| 17 | CHECK (create (file_name, 0), "create \"%s\"", file_name); | ||
| 18 | CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); | ||
| 19 | msg ("seek \"%s\"", file_name); | ||
| 20 | seek (fd, sizeof buf - 1); | ||
| 21 | CHECK (write (fd, &zero, 1) > 0, "write \"%s\"", file_name); | ||
| 22 | msg ("close \"%s\"", file_name); | ||
| 23 | close (fd); | ||
| 24 | check_file (file_name, buf, sizeof buf); | ||
| 25 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-sparse.ck b/pintos-progos/tests/filesys/extended/grow-sparse.ck new file mode 100644 index 0000000..379ba2c --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-sparse.ck | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (grow-sparse) begin | ||
| 7 | (grow-sparse) create "testfile" | ||
| 8 | (grow-sparse) open "testfile" | ||
| 9 | (grow-sparse) seek "testfile" | ||
| 10 | (grow-sparse) write "testfile" | ||
| 11 | (grow-sparse) close "testfile" | ||
| 12 | (grow-sparse) open "testfile" for verification | ||
| 13 | (grow-sparse) verified contents of "testfile" | ||
| 14 | (grow-sparse) close "testfile" | ||
| 15 | (grow-sparse) end | ||
| 16 | EOF | ||
| 17 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-tell-persistence.ck b/pintos-progos/tests/filesys/extended/grow-tell-persistence.ck new file mode 100644 index 0000000..d93a422 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-tell-persistence.ck | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | check_archive ({"foobar" => [random_bytes (2134)]}); | ||
| 7 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-tell.c b/pintos-progos/tests/filesys/extended/grow-tell.c new file mode 100644 index 0000000..5f5da5b --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-tell.c | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | /* Checks that growing a file updates the file position | ||
| 2 | correctly. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/filesys/seq-test.h" | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | static char buf[2134]; | ||
| 10 | |||
| 11 | static size_t | ||
| 12 | return_block_size (void) | ||
| 13 | { | ||
| 14 | return 37; | ||
| 15 | } | ||
| 16 | |||
| 17 | static void | ||
| 18 | check_tell (int fd, long ofs) | ||
| 19 | { | ||
| 20 | long pos = tell (fd); | ||
| 21 | if (pos != ofs) | ||
| 22 | fail ("file position not updated properly: should be %ld, actually %ld", | ||
| 23 | ofs, pos); | ||
| 24 | } | ||
| 25 | |||
| 26 | void | ||
| 27 | test_main (void) | ||
| 28 | { | ||
| 29 | seq_test ("foobar", | ||
| 30 | buf, sizeof buf, 0, | ||
| 31 | return_block_size, check_tell); | ||
| 32 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-tell.ck b/pintos-progos/tests/filesys/extended/grow-tell.ck new file mode 100644 index 0000000..fe94707 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-tell.ck | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 7 | (grow-tell) begin | ||
| 8 | (grow-tell) create "foobar" | ||
| 9 | (grow-tell) open "foobar" | ||
| 10 | (grow-tell) writing "foobar" | ||
| 11 | (grow-tell) close "foobar" | ||
| 12 | (grow-tell) open "foobar" for verification | ||
| 13 | (grow-tell) verified contents of "foobar" | ||
| 14 | (grow-tell) close "foobar" | ||
| 15 | (grow-tell) end | ||
| 16 | EOF | ||
| 17 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-two-files-persistence.ck b/pintos-progos/tests/filesys/extended/grow-two-files-persistence.ck new file mode 100644 index 0000000..1c4ced1 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-two-files-persistence.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | my ($a) = random_bytes (8143); | ||
| 7 | my ($b) = random_bytes (8143); | ||
| 8 | check_archive ({"a" => [$a], "b" => [$b]}); | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-two-files.c b/pintos-progos/tests/filesys/extended/grow-two-files.c new file mode 100644 index 0000000..6a8fb1c --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-two-files.c | |||
| @@ -0,0 +1,62 @@ | |||
| 1 | /* Grows two files in parallel and checks that their contents are | ||
| 2 | correct. */ | ||
| 3 | |||
| 4 | #include <random.h> | ||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | #define FILE_SIZE 8143 | ||
| 10 | static char buf_a[FILE_SIZE]; | ||
| 11 | static char buf_b[FILE_SIZE]; | ||
| 12 | |||
| 13 | static void | ||
| 14 | write_some_bytes (const char *file_name, int fd, const char *buf, size_t *ofs) | ||
| 15 | { | ||
| 16 | if (*ofs < FILE_SIZE) | ||
| 17 | { | ||
| 18 | size_t block_size = random_ulong () % (FILE_SIZE / 8) + 1; | ||
| 19 | size_t ret_val; | ||
| 20 | if (block_size > FILE_SIZE - *ofs) | ||
| 21 | block_size = FILE_SIZE - *ofs; | ||
| 22 | |||
| 23 | ret_val = write (fd, buf + *ofs, block_size); | ||
| 24 | if (ret_val != block_size) | ||
| 25 | fail ("write %zu bytes at offset %zu in \"%s\" returned %zu", | ||
| 26 | block_size, *ofs, file_name, ret_val); | ||
| 27 | *ofs += block_size; | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | void | ||
| 32 | test_main (void) | ||
| 33 | { | ||
| 34 | int fd_a, fd_b; | ||
| 35 | size_t ofs_a = 0, ofs_b = 0; | ||
| 36 | |||
| 37 | random_init (0); | ||
| 38 | random_bytes (buf_a, sizeof buf_a); | ||
| 39 | random_bytes (buf_b, sizeof buf_b); | ||
| 40 | |||
| 41 | CHECK (create ("a", 0), "create \"a\""); | ||
| 42 | CHECK (create ("b", 0), "create \"b\""); | ||
| 43 | |||
| 44 | CHECK ((fd_a = open ("a")) > 1, "open \"a\""); | ||
| 45 | CHECK ((fd_b = open ("b")) > 1, "open \"b\""); | ||
| 46 | |||
| 47 | msg ("write \"a\" and \"b\" alternately"); | ||
| 48 | while (ofs_a < FILE_SIZE || ofs_b < FILE_SIZE) | ||
| 49 | { | ||
| 50 | write_some_bytes ("a", fd_a, buf_a, &ofs_a); | ||
| 51 | write_some_bytes ("b", fd_b, buf_b, &ofs_b); | ||
| 52 | } | ||
| 53 | |||
| 54 | msg ("close \"a\""); | ||
| 55 | close (fd_a); | ||
| 56 | |||
| 57 | msg ("close \"b\""); | ||
| 58 | close (fd_b); | ||
| 59 | |||
| 60 | check_file ("a", buf_a, FILE_SIZE); | ||
| 61 | check_file ("b", buf_b, FILE_SIZE); | ||
| 62 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-two-files.ck b/pintos-progos/tests/filesys/extended/grow-two-files.ck new file mode 100644 index 0000000..b5e754a --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-two-files.ck | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 7 | (grow-two-files) begin | ||
| 8 | (grow-two-files) create "a" | ||
| 9 | (grow-two-files) create "b" | ||
| 10 | (grow-two-files) open "a" | ||
| 11 | (grow-two-files) open "b" | ||
| 12 | (grow-two-files) write "a" and "b" alternately | ||
| 13 | (grow-two-files) close "a" | ||
| 14 | (grow-two-files) close "b" | ||
| 15 | (grow-two-files) open "a" for verification | ||
| 16 | (grow-two-files) verified contents of "a" | ||
| 17 | (grow-two-files) close "a" | ||
| 18 | (grow-two-files) open "b" for verification | ||
| 19 | (grow-two-files) verified contents of "b" | ||
| 20 | (grow-two-files) close "b" | ||
| 21 | (grow-two-files) end | ||
| 22 | EOF | ||
| 23 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/mk-tree.c b/pintos-progos/tests/filesys/extended/mk-tree.c new file mode 100644 index 0000000..a36bb88 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/mk-tree.c | |||
| @@ -0,0 +1,67 @@ | |||
| 1 | /* Library function for creating a tree of directories. */ | ||
| 2 | |||
| 3 | #include <stdio.h> | ||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/filesys/extended/mk-tree.h" | ||
| 6 | #include "tests/lib.h" | ||
| 7 | |||
| 8 | static void do_mkdir (const char *format, ...) PRINTF_FORMAT (1, 2); | ||
| 9 | static void do_touch (const char *format, ...) PRINTF_FORMAT (1, 2); | ||
| 10 | |||
| 11 | void | ||
| 12 | make_tree (int at, int bt, int ct, int dt) | ||
| 13 | { | ||
| 14 | char try[128]; | ||
| 15 | int a, b, c, d; | ||
| 16 | int fd; | ||
| 17 | |||
| 18 | msg ("creating /0/0/0/0 through /%d/%d/%d/%d...", | ||
| 19 | at - 1, bt - 1, ct - 1, dt - 1); | ||
| 20 | quiet = true; | ||
| 21 | for (a = 0; a < at; a++) | ||
| 22 | { | ||
| 23 | do_mkdir ("/%d", a); | ||
| 24 | for (b = 0; b < bt; b++) | ||
| 25 | { | ||
| 26 | do_mkdir ("/%d/%d", a, b); | ||
| 27 | for (c = 0; c < ct; c++) | ||
| 28 | { | ||
| 29 | do_mkdir ("/%d/%d/%d", a, b, c); | ||
| 30 | for (d = 0; d < dt; d++) | ||
| 31 | do_touch ("/%d/%d/%d/%d", a, b, c, d); | ||
| 32 | } | ||
| 33 | } | ||
| 34 | } | ||
| 35 | quiet = false; | ||
| 36 | |||
| 37 | snprintf (try, sizeof try, "/%d/%d/%d/%d", 0, bt - 1, 0, dt - 1); | ||
| 38 | CHECK ((fd = open (try)) > 1, "open \"%s\"", try); | ||
| 39 | msg ("close \"%s\"", try); | ||
| 40 | close (fd); | ||
| 41 | } | ||
| 42 | |||
| 43 | static void | ||
| 44 | do_mkdir (const char *format, ...) | ||
| 45 | { | ||
| 46 | char dir[128]; | ||
| 47 | va_list args; | ||
| 48 | |||
| 49 | va_start (args, format); | ||
| 50 | vsnprintf (dir, sizeof dir, format, args); | ||
| 51 | va_end (args); | ||
| 52 | |||
| 53 | CHECK (mkdir (dir), "mkdir \"%s\"", dir); | ||
| 54 | } | ||
| 55 | |||
| 56 | static void | ||
| 57 | do_touch (const char *format, ...) | ||
| 58 | { | ||
| 59 | char file[128]; | ||
| 60 | va_list args; | ||
| 61 | |||
| 62 | va_start (args, format); | ||
| 63 | vsnprintf (file, sizeof file, format, args); | ||
| 64 | va_end (args); | ||
| 65 | |||
| 66 | CHECK (create (file, 0), "create \"%s\"", file); | ||
| 67 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/mk-tree.h b/pintos-progos/tests/filesys/extended/mk-tree.h new file mode 100644 index 0000000..df0d5a6 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/mk-tree.h | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | #ifndef TESTS_FILESYS_EXTENDED_MK_TREE_H | ||
| 2 | #define TESTS_FILESYS_EXTENDED_MK_TREE_H | ||
| 3 | |||
| 4 | void make_tree (int at, int bt, int ct, int dt); | ||
| 5 | |||
| 6 | #endif /* tests/filesys/extended/mk-tree.h */ | ||
diff --git a/pintos-progos/tests/filesys/extended/syn-rw-persistence.ck b/pintos-progos/tests/filesys/extended/syn-rw-persistence.ck new file mode 100644 index 0000000..62d57ee --- /dev/null +++ b/pintos-progos/tests/filesys/extended/syn-rw-persistence.ck | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | check_archive ({"child-syn-rw" => "tests/filesys/extended/child-syn-rw", | ||
| 7 | "logfile" => [random_bytes (8 * 512)]}); | ||
| 8 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/syn-rw.c b/pintos-progos/tests/filesys/extended/syn-rw.c new file mode 100644 index 0000000..657dfb5 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/syn-rw.c | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | /* Grows a file in chunks while subprocesses read the growing | ||
| 2 | file. */ | ||
| 3 | |||
| 4 | #include <random.h> | ||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/filesys/extended/syn-rw.h" | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | char buf[BUF_SIZE]; | ||
| 11 | |||
| 12 | #define CHILD_CNT 4 | ||
| 13 | |||
| 14 | void | ||
| 15 | test_main (void) | ||
| 16 | { | ||
| 17 | pid_t children[CHILD_CNT]; | ||
| 18 | size_t ofs; | ||
| 19 | int fd; | ||
| 20 | |||
| 21 | CHECK (create (file_name, 0), "create \"%s\"", file_name); | ||
| 22 | CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); | ||
| 23 | |||
| 24 | exec_children ("child-syn-rw", children, CHILD_CNT); | ||
| 25 | |||
| 26 | random_bytes (buf, sizeof buf); | ||
| 27 | quiet = true; | ||
| 28 | for (ofs = 0; ofs < BUF_SIZE; ofs += CHUNK_SIZE) | ||
| 29 | CHECK (write (fd, buf + ofs, CHUNK_SIZE) > 0, | ||
| 30 | "write %d bytes at offset %zu in \"%s\"", | ||
| 31 | (int) CHUNK_SIZE, ofs, file_name); | ||
| 32 | quiet = false; | ||
| 33 | |||
| 34 | wait_children (children, CHILD_CNT); | ||
| 35 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/syn-rw.ck b/pintos-progos/tests/filesys/extended/syn-rw.ck new file mode 100644 index 0000000..ac82aa8 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/syn-rw.ck | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 7 | (syn-rw) begin | ||
| 8 | (syn-rw) create "logfile" | ||
| 9 | (syn-rw) open "logfile" | ||
| 10 | (syn-rw) exec child 1 of 4: "child-syn-rw 0" | ||
| 11 | (syn-rw) exec child 2 of 4: "child-syn-rw 1" | ||
| 12 | (syn-rw) exec child 3 of 4: "child-syn-rw 2" | ||
| 13 | (syn-rw) exec child 4 of 4: "child-syn-rw 3" | ||
| 14 | (syn-rw) wait for child 1 of 4 returned 0 (expected 0) | ||
| 15 | (syn-rw) wait for child 2 of 4 returned 1 (expected 1) | ||
| 16 | (syn-rw) wait for child 3 of 4 returned 2 (expected 2) | ||
| 17 | (syn-rw) wait for child 4 of 4 returned 3 (expected 3) | ||
| 18 | (syn-rw) end | ||
| 19 | EOF | ||
| 20 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/syn-rw.h b/pintos-progos/tests/filesys/extended/syn-rw.h new file mode 100644 index 0000000..170aeb4 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/syn-rw.h | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | #ifndef TESTS_FILESYS_EXTENDED_SYN_RW_H | ||
| 2 | #define TESTS_FILESYS_EXTENDED_SYN_RW_H | ||
| 3 | |||
| 4 | #define CHUNK_SIZE 8 | ||
| 5 | #define CHUNK_CNT 512 | ||
| 6 | #define BUF_SIZE (CHUNK_SIZE * CHUNK_CNT) | ||
| 7 | static const char file_name[] = "logfile"; | ||
| 8 | |||
| 9 | #endif /* tests/filesys/extended/syn-rw.h */ | ||
diff --git a/pintos-progos/tests/filesys/extended/tar.c b/pintos-progos/tests/filesys/extended/tar.c new file mode 100644 index 0000000..9801484 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/tar.c | |||
| @@ -0,0 +1,208 @@ | |||
| 1 | /* tar.c | ||
| 2 | |||
| 3 | Creates a tar archive. */ | ||
| 4 | |||
| 5 | #include <ustar.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | #include <stdio.h> | ||
| 8 | #include <string.h> | ||
| 9 | |||
| 10 | static void usage (void); | ||
| 11 | static bool make_tar_archive (const char *archive_name, | ||
| 12 | char *files[], size_t file_cnt); | ||
| 13 | |||
| 14 | int | ||
| 15 | main (int argc, char *argv[]) | ||
| 16 | { | ||
| 17 | if (argc < 3) | ||
| 18 | usage (); | ||
| 19 | |||
| 20 | return (make_tar_archive (argv[1], argv + 2, argc - 2) | ||
| 21 | ? EXIT_SUCCESS : EXIT_FAILURE); | ||
| 22 | } | ||
| 23 | |||
| 24 | static void | ||
| 25 | usage (void) | ||
| 26 | { | ||
| 27 | printf ("tar, tar archive creator\n" | ||
| 28 | "Usage: tar ARCHIVE FILE...\n" | ||
| 29 | "where ARCHIVE is the tar archive to create\n" | ||
| 30 | " and FILE... is a list of files or directories to put into it.\n" | ||
| 31 | "(ARCHIVE itself will not be included in the archive, even if it\n" | ||
| 32 | "is in a directory to be archived.)\n"); | ||
| 33 | exit (EXIT_FAILURE); | ||
| 34 | } | ||
| 35 | |||
| 36 | static bool archive_file (char file_name[], size_t file_name_size, | ||
| 37 | int archive_fd, bool *write_error); | ||
| 38 | |||
| 39 | static bool archive_ordinary_file (const char *file_name, int file_fd, | ||
| 40 | int archive_fd, bool *write_error); | ||
| 41 | static bool archive_directory (char file_name[], size_t file_name_size, | ||
| 42 | int file_fd, int archive_fd, bool *write_error); | ||
| 43 | static bool write_header (const char *file_name, enum ustar_type, int size, | ||
| 44 | int archive_fd, bool *write_error); | ||
| 45 | |||
| 46 | static bool do_write (int fd, const char *buffer, int size, bool *write_error); | ||
| 47 | |||
| 48 | static bool | ||
| 49 | make_tar_archive (const char *archive_name, char *files[], size_t file_cnt) | ||
| 50 | { | ||
| 51 | static const char zeros[512]; | ||
| 52 | int archive_fd; | ||
| 53 | bool success = true; | ||
| 54 | bool write_error = false; | ||
| 55 | size_t i; | ||
| 56 | |||
| 57 | if (!create (archive_name, 0)) | ||
| 58 | { | ||
| 59 | printf ("%s: create failed\n", archive_name); | ||
| 60 | return false; | ||
| 61 | } | ||
| 62 | archive_fd = open (archive_name); | ||
| 63 | if (archive_fd < 0) | ||
| 64 | { | ||
| 65 | printf ("%s: open failed\n", archive_name); | ||
| 66 | return false; | ||
| 67 | } | ||
| 68 | |||
| 69 | for (i = 0; i < file_cnt; i++) | ||
| 70 | { | ||
| 71 | char file_name[128]; | ||
| 72 | |||
| 73 | strlcpy (file_name, files[i], sizeof file_name); | ||
| 74 | if (!archive_file (file_name, sizeof file_name, | ||
| 75 | archive_fd, &write_error)) | ||
| 76 | success = false; | ||
| 77 | } | ||
| 78 | |||
| 79 | if (!do_write (archive_fd, zeros, 512, &write_error) | ||
| 80 | || !do_write (archive_fd, zeros, 512, &write_error)) | ||
| 81 | success = false; | ||
| 82 | |||
| 83 | close (archive_fd); | ||
| 84 | |||
| 85 | return success; | ||
| 86 | } | ||
| 87 | |||
| 88 | static bool | ||
| 89 | archive_file (char file_name[], size_t file_name_size, | ||
| 90 | int archive_fd, bool *write_error) | ||
| 91 | { | ||
| 92 | int file_fd = open (file_name); | ||
| 93 | if (file_fd >= 0) | ||
| 94 | { | ||
| 95 | bool success; | ||
| 96 | |||
| 97 | if (inumber (file_fd) != inumber (archive_fd)) | ||
| 98 | { | ||
| 99 | if (!isdir (file_fd)) | ||
| 100 | success = archive_ordinary_file (file_name, file_fd, | ||
| 101 | archive_fd, write_error); | ||
| 102 | else | ||
| 103 | success = archive_directory (file_name, file_name_size, file_fd, | ||
| 104 | archive_fd, write_error); | ||
| 105 | } | ||
| 106 | else | ||
| 107 | { | ||
| 108 | /* Nothing to do: don't try to archive the archive file. */ | ||
| 109 | success = true; | ||
| 110 | } | ||
| 111 | |||
| 112 | close (file_fd); | ||
| 113 | |||
| 114 | return success; | ||
| 115 | } | ||
| 116 | else | ||
| 117 | { | ||
| 118 | printf ("%s: open failed\n", file_name); | ||
| 119 | return false; | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | static bool | ||
| 124 | archive_ordinary_file (const char *file_name, int file_fd, | ||
| 125 | int archive_fd, bool *write_error) | ||
| 126 | { | ||
| 127 | bool read_error = false; | ||
| 128 | bool success = true; | ||
| 129 | int file_size = filesize (file_fd); | ||
| 130 | |||
| 131 | if (!write_header (file_name, USTAR_REGULAR, file_size, | ||
| 132 | archive_fd, write_error)) | ||
| 133 | return false; | ||
| 134 | |||
| 135 | while (file_size > 0) | ||
| 136 | { | ||
| 137 | static char buf[512]; | ||
| 138 | int chunk_size = file_size > 512 ? 512 : file_size; | ||
| 139 | int read_retval = read (file_fd, buf, chunk_size); | ||
| 140 | int bytes_read = read_retval > 0 ? read_retval : 0; | ||
| 141 | |||
| 142 | if (bytes_read != chunk_size && !read_error) | ||
| 143 | { | ||
| 144 | printf ("%s: read error\n", file_name); | ||
| 145 | read_error = true; | ||
| 146 | success = false; | ||
| 147 | } | ||
| 148 | |||
| 149 | memset (buf + bytes_read, 0, 512 - bytes_read); | ||
| 150 | if (!do_write (archive_fd, buf, 512, write_error)) | ||
| 151 | success = false; | ||
| 152 | |||
| 153 | file_size -= chunk_size; | ||
| 154 | } | ||
| 155 | |||
| 156 | return success; | ||
| 157 | } | ||
| 158 | |||
| 159 | static bool | ||
| 160 | archive_directory (char file_name[], size_t file_name_size, int file_fd, | ||
| 161 | int archive_fd, bool *write_error) | ||
| 162 | { | ||
| 163 | size_t dir_len; | ||
| 164 | bool success = true; | ||
| 165 | |||
| 166 | dir_len = strlen (file_name); | ||
| 167 | if (dir_len + 1 + READDIR_MAX_LEN + 1 > file_name_size) | ||
| 168 | { | ||
| 169 | printf ("%s: file name too long\n", file_name); | ||
| 170 | return false; | ||
| 171 | } | ||
| 172 | |||
| 173 | if (!write_header (file_name, USTAR_DIRECTORY, 0, archive_fd, write_error)) | ||
| 174 | return false; | ||
| 175 | |||
| 176 | file_name[dir_len] = '/'; | ||
| 177 | while (readdir (file_fd, &file_name[dir_len + 1])) | ||
| 178 | if (!archive_file (file_name, file_name_size, archive_fd, write_error)) | ||
| 179 | success = false; | ||
| 180 | file_name[dir_len] = '\0'; | ||
| 181 | |||
| 182 | return success; | ||
| 183 | } | ||
| 184 | |||
| 185 | static bool | ||
| 186 | write_header (const char *file_name, enum ustar_type type, int size, | ||
| 187 | int archive_fd, bool *write_error) | ||
| 188 | { | ||
| 189 | static char header[512]; | ||
| 190 | return (ustar_make_header (file_name, type, size, header) | ||
| 191 | && do_write (archive_fd, header, 512, write_error)); | ||
| 192 | } | ||
| 193 | |||
| 194 | static bool | ||
| 195 | do_write (int fd, const char *buffer, int size, bool *write_error) | ||
| 196 | { | ||
| 197 | if (write (fd, buffer, size) == size) | ||
| 198 | return true; | ||
| 199 | else | ||
| 200 | { | ||
| 201 | if (!*write_error) | ||
| 202 | { | ||
| 203 | printf ("error writing archive\n"); | ||
| 204 | *write_error = true; | ||
| 205 | } | ||
| 206 | return false; | ||
| 207 | } | ||
| 208 | } | ||
diff --git a/pintos-progos/tests/filesys/seq-test.c b/pintos-progos/tests/filesys/seq-test.c new file mode 100644 index 0000000..8ce222c --- /dev/null +++ b/pintos-progos/tests/filesys/seq-test.c | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | #include "tests/filesys/seq-test.h" | ||
| 2 | #include <random.h> | ||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | |||
| 6 | void | ||
| 7 | seq_test (const char *file_name, void *buf, size_t size, size_t initial_size, | ||
| 8 | size_t (*block_size_func) (void), | ||
| 9 | void (*check_func) (int fd, long ofs)) | ||
| 10 | { | ||
| 11 | size_t ofs; | ||
| 12 | int fd; | ||
| 13 | |||
| 14 | random_bytes (buf, size); | ||
| 15 | CHECK (create (file_name, initial_size), "create \"%s\"", file_name); | ||
| 16 | CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); | ||
| 17 | |||
| 18 | ofs = 0; | ||
| 19 | msg ("writing \"%s\"", file_name); | ||
| 20 | while (ofs < size) | ||
| 21 | { | ||
| 22 | size_t block_size = block_size_func (); | ||
| 23 | if (block_size > size - ofs) | ||
| 24 | block_size = size - ofs; | ||
| 25 | |||
| 26 | if (write (fd, buf + ofs, block_size) != (int) block_size) | ||
| 27 | fail ("write %zu bytes at offset %zu in \"%s\" failed", | ||
| 28 | block_size, ofs, file_name); | ||
| 29 | |||
| 30 | ofs += block_size; | ||
| 31 | if (check_func != NULL) | ||
| 32 | check_func (fd, ofs); | ||
| 33 | } | ||
| 34 | msg ("close \"%s\"", file_name); | ||
| 35 | close (fd); | ||
| 36 | check_file (file_name, buf, size); | ||
| 37 | } | ||
diff --git a/pintos-progos/tests/filesys/seq-test.h b/pintos-progos/tests/filesys/seq-test.h new file mode 100644 index 0000000..0697381 --- /dev/null +++ b/pintos-progos/tests/filesys/seq-test.h | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | #ifndef TESTS_FILESYS_SEQ_TEST_H | ||
| 2 | #define TESTS_FILESYS_SEQ_TEST_H | ||
| 3 | |||
| 4 | #include <stddef.h> | ||
| 5 | |||
| 6 | void seq_test (const char *file_name, | ||
| 7 | void *buf, size_t size, size_t initial_size, | ||
| 8 | size_t (*block_size_func) (void), | ||
| 9 | void (*check_func) (int fd, long ofs)); | ||
| 10 | |||
| 11 | #endif /* tests/filesys/seq-test.h */ | ||
diff --git a/pintos-progos/tests/internal/list.c b/pintos-progos/tests/internal/list.c new file mode 100644 index 0000000..836c69e --- /dev/null +++ b/pintos-progos/tests/internal/list.c | |||
| @@ -0,0 +1,174 @@ | |||
| 1 | /* Test program for lib/kernel/list.c. | ||
| 2 | |||
| 3 | Attempts to test the list functionality that is not | ||
| 4 | sufficiently tested elsewhere in Pintos. | ||
| 5 | |||
| 6 | This is not a test we will run on your submitted projects. | ||
| 7 | It is here for completeness. | ||
| 8 | */ | ||
| 9 | |||
| 10 | #undef NDEBUG | ||
| 11 | #include <debug.h> | ||
| 12 | #include <list.h> | ||
| 13 | #include <random.h> | ||
| 14 | #include <stdio.h> | ||
| 15 | #include "threads/test.h" | ||
| 16 | |||
| 17 | /* Maximum number of elements in a linked list that we will | ||
| 18 | test. */ | ||
| 19 | #define MAX_SIZE 64 | ||
| 20 | |||
| 21 | /* A linked list element. */ | ||
| 22 | struct value | ||
| 23 | { | ||
| 24 | struct list_elem elem; /* List element. */ | ||
| 25 | int value; /* Item value. */ | ||
| 26 | }; | ||
| 27 | |||
| 28 | static void shuffle (struct value[], size_t); | ||
| 29 | static bool value_less (const struct list_elem *, const struct list_elem *, | ||
| 30 | void *); | ||
| 31 | static void verify_list_fwd (struct list *, int size); | ||
| 32 | static void verify_list_bkwd (struct list *, int size); | ||
| 33 | |||
| 34 | /* Test the linked list implementation. */ | ||
| 35 | void | ||
| 36 | test (void) | ||
| 37 | { | ||
| 38 | int size; | ||
| 39 | |||
| 40 | printf ("testing various size lists:"); | ||
| 41 | for (size = 0; size < MAX_SIZE; size++) | ||
| 42 | { | ||
| 43 | int repeat; | ||
| 44 | |||
| 45 | printf (" %d", size); | ||
| 46 | for (repeat = 0; repeat < 10; repeat++) | ||
| 47 | { | ||
| 48 | static struct value values[MAX_SIZE * 4]; | ||
| 49 | struct list list; | ||
| 50 | struct list_elem *e; | ||
| 51 | int i, ofs; | ||
| 52 | |||
| 53 | /* Put values 0...SIZE in random order in VALUES. */ | ||
| 54 | for (i = 0; i < size; i++) | ||
| 55 | values[i].value = i; | ||
| 56 | shuffle (values, size); | ||
| 57 | |||
| 58 | /* Assemble list. */ | ||
| 59 | list_init (&list); | ||
| 60 | for (i = 0; i < size; i++) | ||
| 61 | list_push_back (&list, &values[i].elem); | ||
| 62 | |||
| 63 | /* Verify correct minimum and maximum elements. */ | ||
| 64 | e = list_min (&list, value_less, NULL); | ||
| 65 | ASSERT (size ? list_entry (e, struct value, elem)->value == 0 | ||
| 66 | : e == list_begin (&list)); | ||
| 67 | e = list_max (&list, value_less, NULL); | ||
| 68 | ASSERT (size ? list_entry (e, struct value, elem)->value == size - 1 | ||
| 69 | : e == list_begin (&list)); | ||
| 70 | |||
| 71 | /* Sort and verify list. */ | ||
| 72 | list_sort (&list, value_less, NULL); | ||
| 73 | verify_list_fwd (&list, size); | ||
| 74 | |||
| 75 | /* Reverse and verify list. */ | ||
| 76 | list_reverse (&list); | ||
| 77 | verify_list_bkwd (&list, size); | ||
| 78 | |||
| 79 | /* Shuffle, insert using list_insert_ordered(), | ||
| 80 | and verify ordering. */ | ||
| 81 | shuffle (values, size); | ||
| 82 | list_init (&list); | ||
| 83 | for (i = 0; i < size; i++) | ||
| 84 | list_insert_ordered (&list, &values[i].elem, | ||
| 85 | value_less, NULL); | ||
| 86 | verify_list_fwd (&list, size); | ||
| 87 | |||
| 88 | /* Duplicate some items, uniquify, and verify. */ | ||
| 89 | ofs = size; | ||
| 90 | for (e = list_begin (&list); e != list_end (&list); | ||
| 91 | e = list_next (e)) | ||
| 92 | { | ||
| 93 | struct value *v = list_entry (e, struct value, elem); | ||
| 94 | int copies = random_ulong () % 4; | ||
| 95 | while (copies-- > 0) | ||
| 96 | { | ||
| 97 | values[ofs].value = v->value; | ||
| 98 | list_insert (e, &values[ofs++].elem); | ||
| 99 | } | ||
| 100 | } | ||
| 101 | ASSERT ((size_t) ofs < sizeof values / sizeof *values); | ||
| 102 | list_unique (&list, NULL, value_less, NULL); | ||
| 103 | verify_list_fwd (&list, size); | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | printf (" done\n"); | ||
| 108 | printf ("list: PASS\n"); | ||
| 109 | } | ||
| 110 | |||
| 111 | /* Shuffles the CNT elements in ARRAY into random order. */ | ||
| 112 | static void | ||
| 113 | shuffle (struct value *array, size_t cnt) | ||
| 114 | { | ||
| 115 | size_t i; | ||
| 116 | |||
| 117 | for (i = 0; i < cnt; i++) | ||
| 118 | { | ||
| 119 | size_t j = i + random_ulong () % (cnt - i); | ||
| 120 | struct value t = array[j]; | ||
| 121 | array[j] = array[i]; | ||
| 122 | array[i] = t; | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | /* Returns true if value A is less than value B, false | ||
| 127 | otherwise. */ | ||
| 128 | static bool | ||
| 129 | value_less (const struct list_elem *a_, const struct list_elem *b_, | ||
| 130 | void *aux UNUSED) | ||
| 131 | { | ||
| 132 | const struct value *a = list_entry (a_, struct value, elem); | ||
| 133 | const struct value *b = list_entry (b_, struct value, elem); | ||
| 134 | |||
| 135 | return a->value < b->value; | ||
| 136 | } | ||
| 137 | |||
| 138 | /* Verifies that LIST contains the values 0...SIZE when traversed | ||
| 139 | in forward order. */ | ||
| 140 | static void | ||
| 141 | verify_list_fwd (struct list *list, int size) | ||
| 142 | { | ||
| 143 | struct list_elem *e; | ||
| 144 | int i; | ||
| 145 | |||
| 146 | for (i = 0, e = list_begin (list); | ||
| 147 | i < size && e != list_end (list); | ||
| 148 | i++, e = list_next (e)) | ||
| 149 | { | ||
| 150 | struct value *v = list_entry (e, struct value, elem); | ||
| 151 | ASSERT (i == v->value); | ||
| 152 | } | ||
| 153 | ASSERT (i == size); | ||
| 154 | ASSERT (e == list_end (list)); | ||
| 155 | } | ||
| 156 | |||
| 157 | /* Verifies that LIST contains the values 0...SIZE when traversed | ||
| 158 | in reverse order. */ | ||
| 159 | static void | ||
| 160 | verify_list_bkwd (struct list *list, int size) | ||
| 161 | { | ||
| 162 | struct list_elem *e; | ||
| 163 | int i; | ||
| 164 | |||
| 165 | for (i = 0, e = list_rbegin (list); | ||
| 166 | i < size && e != list_rend (list); | ||
| 167 | i++, e = list_prev (e)) | ||
| 168 | { | ||
| 169 | struct value *v = list_entry (e, struct value, elem); | ||
| 170 | ASSERT (i == v->value); | ||
| 171 | } | ||
| 172 | ASSERT (i == size); | ||
| 173 | ASSERT (e == list_rend (list)); | ||
| 174 | } | ||
diff --git a/pintos-progos/tests/internal/stdio.c b/pintos-progos/tests/internal/stdio.c new file mode 100644 index 0000000..fb60cda --- /dev/null +++ b/pintos-progos/tests/internal/stdio.c | |||
| @@ -0,0 +1,208 @@ | |||
| 1 | /* Test program for printf() in lib/stdio.c. | ||
| 2 | |||
| 3 | Attempts to test printf() functionality that is not | ||
| 4 | sufficiently tested elsewhere in Pintos. | ||
| 5 | |||
| 6 | This is not a test we will run on your submitted projects. | ||
| 7 | It is here for completeness. | ||
| 8 | */ | ||
| 9 | |||
| 10 | #undef NDEBUG | ||
| 11 | #include <limits.h> | ||
| 12 | #include <stdarg.h> | ||
| 13 | #include <stddef.h> | ||
| 14 | #include <stdlib.h> | ||
| 15 | #include <stdio.h> | ||
| 16 | #include <string.h> | ||
| 17 | #include "threads/test.h" | ||
| 18 | |||
| 19 | /* Number of failures so far. */ | ||
| 20 | static int failure_cnt; | ||
| 21 | |||
| 22 | static void | ||
| 23 | checkf (const char *expect, const char *format, ...) | ||
| 24 | { | ||
| 25 | char output[128]; | ||
| 26 | va_list args; | ||
| 27 | |||
| 28 | printf ("\"%s\" -> \"%s\": ", format, expect); | ||
| 29 | |||
| 30 | va_start (args, format); | ||
| 31 | vsnprintf (output, sizeof output, format, args); | ||
| 32 | va_end (args); | ||
| 33 | |||
| 34 | if (strcmp (expect, output)) | ||
| 35 | { | ||
| 36 | printf ("\nFAIL: actual output \"%s\"\n", output); | ||
| 37 | failure_cnt++; | ||
| 38 | } | ||
| 39 | else | ||
| 40 | printf ("okay\n"); | ||
| 41 | } | ||
| 42 | |||
| 43 | /* Test printf() implementation. */ | ||
| 44 | void | ||
| 45 | test (void) | ||
| 46 | { | ||
| 47 | printf ("Testing formats:"); | ||
| 48 | |||
| 49 | /* Check that commas show up in the right places, for positive | ||
| 50 | numbers. */ | ||
| 51 | checkf ("1", "%'d", 1); | ||
| 52 | checkf ("12", "%'d", 12); | ||
| 53 | checkf ("123", "%'d", 123); | ||
| 54 | checkf ("1,234", "%'d", 1234); | ||
| 55 | checkf ("12,345", "%'d", 12345); | ||
| 56 | checkf ("123,456", "%'ld", 123456L); | ||
| 57 | checkf ("1,234,567", "%'ld", 1234567L); | ||
| 58 | checkf ("12,345,678", "%'ld", 12345678L); | ||
| 59 | checkf ("123,456,789", "%'ld", 123456789L); | ||
| 60 | checkf ("1,234,567,890", "%'ld", 1234567890L); | ||
| 61 | checkf ("12,345,678,901", "%'lld", 12345678901LL); | ||
| 62 | checkf ("123,456,789,012", "%'lld", 123456789012LL); | ||
| 63 | checkf ("1,234,567,890,123", "%'lld", 1234567890123LL); | ||
| 64 | checkf ("12,345,678,901,234", "%'lld", 12345678901234LL); | ||
| 65 | checkf ("123,456,789,012,345", "%'lld", 123456789012345LL); | ||
| 66 | checkf ("1,234,567,890,123,456", "%'lld", 1234567890123456LL); | ||
| 67 | checkf ("12,345,678,901,234,567", "%'lld", 12345678901234567LL); | ||
| 68 | checkf ("123,456,789,012,345,678", "%'lld", 123456789012345678LL); | ||
| 69 | checkf ("1,234,567,890,123,456,789", "%'lld", 1234567890123456789LL); | ||
| 70 | |||
| 71 | /* Check that commas show up in the right places, for positive | ||
| 72 | numbers. */ | ||
| 73 | checkf ("-1", "%'d", -1); | ||
| 74 | checkf ("-12", "%'d", -12); | ||
| 75 | checkf ("-123", "%'d", -123); | ||
| 76 | checkf ("-1,234", "%'d", -1234); | ||
| 77 | checkf ("-12,345", "%'d", -12345); | ||
| 78 | checkf ("-123,456", "%'ld", -123456L); | ||
| 79 | checkf ("-1,234,567", "%'ld", -1234567L); | ||
| 80 | checkf ("-12,345,678", "%'ld", -12345678L); | ||
| 81 | checkf ("-123,456,789", "%'ld", -123456789L); | ||
| 82 | checkf ("-1,234,567,890", "%'ld", -1234567890L); | ||
| 83 | checkf ("-12,345,678,901", "%'lld", -12345678901LL); | ||
| 84 | checkf ("-123,456,789,012", "%'lld", -123456789012LL); | ||
| 85 | checkf ("-1,234,567,890,123", "%'lld", -1234567890123LL); | ||
| 86 | checkf ("-12,345,678,901,234", "%'lld", -12345678901234LL); | ||
| 87 | checkf ("-123,456,789,012,345", "%'lld", -123456789012345LL); | ||
| 88 | checkf ("-1,234,567,890,123,456", "%'lld", -1234567890123456LL); | ||
| 89 | checkf ("-12,345,678,901,234,567", "%'lld", -12345678901234567LL); | ||
| 90 | checkf ("-123,456,789,012,345,678", "%'lld", -123456789012345678LL); | ||
| 91 | checkf ("-1,234,567,890,123,456,789", "%'lld", -1234567890123456789LL); | ||
| 92 | |||
| 93 | /* Check signed integer conversions. */ | ||
| 94 | checkf (" 0", "%5d", 0); | ||
| 95 | checkf ("0 ", "%-5d", 0); | ||
| 96 | checkf (" +0", "%+5d", 0); | ||
| 97 | checkf ("+0 ", "%+-5d", 0); | ||
| 98 | checkf (" 0", "% 5d", 0); | ||
| 99 | checkf ("00000", "%05d", 0); | ||
| 100 | checkf (" ", "%5.0d", 0); | ||
| 101 | checkf (" 00", "%5.2d", 0); | ||
| 102 | checkf ("0", "%d", 0); | ||
| 103 | |||
| 104 | checkf (" 1", "%5d", 1); | ||
| 105 | checkf ("1 ", "%-5d", 1); | ||
| 106 | checkf (" +1", "%+5d", 1); | ||
| 107 | checkf ("+1 ", "%+-5d", 1); | ||
| 108 | checkf (" 1", "% 5d", 1); | ||
| 109 | checkf ("00001", "%05d", 1); | ||
| 110 | checkf (" 1", "%5.0d", 1); | ||
| 111 | checkf (" 01", "%5.2d", 1); | ||
| 112 | checkf ("1", "%d", 1); | ||
| 113 | |||
| 114 | checkf (" -1", "%5d", -1); | ||
| 115 | checkf ("-1 ", "%-5d", -1); | ||
| 116 | checkf (" -1", "%+5d", -1); | ||
| 117 | checkf ("-1 ", "%+-5d", -1); | ||
| 118 | checkf (" -1", "% 5d", -1); | ||
| 119 | checkf ("-0001", "%05d", -1); | ||
| 120 | checkf (" -1", "%5.0d", -1); | ||
| 121 | checkf (" -01", "%5.2d", -1); | ||
| 122 | checkf ("-1", "%d", -1); | ||
| 123 | |||
| 124 | checkf ("12345", "%5d", 12345); | ||
| 125 | checkf ("12345", "%-5d", 12345); | ||
| 126 | checkf ("+12345", "%+5d", 12345); | ||
| 127 | checkf ("+12345", "%+-5d", 12345); | ||
| 128 | checkf (" 12345", "% 5d", 12345); | ||
| 129 | checkf ("12345", "%05d", 12345); | ||
| 130 | checkf ("12345", "%5.0d", 12345); | ||
| 131 | checkf ("12345", "%5.2d", 12345); | ||
| 132 | checkf ("12345", "%d", 12345); | ||
| 133 | |||
| 134 | checkf ("123456", "%5d", 123456); | ||
| 135 | checkf ("123456", "%-5d", 123456); | ||
| 136 | checkf ("+123456", "%+5d", 123456); | ||
| 137 | checkf ("+123456", "%+-5d", 123456); | ||
| 138 | checkf (" 123456", "% 5d", 123456); | ||
| 139 | checkf ("123456", "%05d", 123456); | ||
| 140 | checkf ("123456", "%5.0d", 123456); | ||
| 141 | checkf ("123456", "%5.2d", 123456); | ||
| 142 | checkf ("123456", "%d", 123456); | ||
| 143 | |||
| 144 | /* Check unsigned integer conversions. */ | ||
| 145 | checkf (" 0", "%5u", 0); | ||
| 146 | checkf (" 0", "%5o", 0); | ||
| 147 | checkf (" 0", "%5x", 0); | ||
| 148 | checkf (" 0", "%5X", 0); | ||
| 149 | checkf (" 0", "%#5o", 0); | ||
| 150 | checkf (" 0", "%#5x", 0); | ||
| 151 | checkf (" 0", "%#5X", 0); | ||
| 152 | checkf (" 00000000", "%#10.8x", 0); | ||
| 153 | |||
| 154 | checkf (" 1", "%5u", 1); | ||
| 155 | checkf (" 1", "%5o", 1); | ||
| 156 | checkf (" 1", "%5x", 1); | ||
| 157 | checkf (" 1", "%5X", 1); | ||
| 158 | checkf (" 01", "%#5o", 1); | ||
| 159 | checkf (" 0x1", "%#5x", 1); | ||
| 160 | checkf (" 0X1", "%#5X", 1); | ||
| 161 | checkf ("0x00000001", "%#10.8x", 1); | ||
| 162 | |||
| 163 | checkf ("123456", "%5u", 123456); | ||
| 164 | checkf ("361100", "%5o", 123456); | ||
| 165 | checkf ("1e240", "%5x", 123456); | ||
| 166 | checkf ("1E240", "%5X", 123456); | ||
| 167 | checkf ("0361100", "%#5o", 123456); | ||
| 168 | checkf ("0x1e240", "%#5x", 123456); | ||
| 169 | checkf ("0X1E240", "%#5X", 123456); | ||
| 170 | checkf ("0x0001e240", "%#10.8x", 123456); | ||
| 171 | |||
| 172 | /* Character and string conversions. */ | ||
| 173 | checkf ("foobar", "%c%c%c%c%c%c", 'f', 'o', 'o', 'b', 'a', 'r'); | ||
| 174 | checkf (" left-right ", "%6s%s%-7s", "left", "-", "right"); | ||
| 175 | checkf ("trim", "%.4s", "trimoff"); | ||
| 176 | checkf ("%%", "%%%%"); | ||
| 177 | |||
| 178 | /* From Cristian Cadar's automatic test case generator. */ | ||
| 179 | checkf (" abcdefgh", "%9s", "abcdefgh"); | ||
| 180 | checkf ("36657730000", "%- o", (unsigned) 036657730000); | ||
| 181 | checkf ("4139757568", "%- u", (unsigned) 4139757568UL); | ||
| 182 | checkf ("f6bfb000", "%- x", (unsigned) 0xf6bfb000); | ||
| 183 | checkf ("36657730000", "%-to", (ptrdiff_t) 036657730000); | ||
| 184 | checkf ("4139757568", "%-tu", (ptrdiff_t) 4139757568UL); | ||
| 185 | checkf ("-155209728", "%-zi", (size_t) -155209728); | ||
| 186 | checkf ("-155209728", "%-zd", (size_t) -155209728); | ||
| 187 | checkf ("036657730000", "%+#o", (unsigned) 036657730000); | ||
| 188 | checkf ("0xf6bfb000", "%+#x", (unsigned) 0xf6bfb000); | ||
| 189 | checkf ("-155209728", "% zi", (size_t) -155209728); | ||
| 190 | checkf ("-155209728", "% zd", (size_t) -155209728); | ||
| 191 | checkf ("4139757568", "% tu", (ptrdiff_t) 4139757568UL); | ||
| 192 | checkf ("036657730000", "% #o", (unsigned) 036657730000); | ||
| 193 | checkf ("0xf6bfb000", "% #x", (unsigned) 0xf6bfb000); | ||
| 194 | checkf ("0xf6bfb000", "%# x", (unsigned) 0xf6bfb000); | ||
| 195 | checkf ("-155209728", "%#zd", (size_t) -155209728); | ||
| 196 | checkf ("-155209728", "%0zi", (size_t) -155209728); | ||
| 197 | checkf ("4,139,757,568", "%'tu", (ptrdiff_t) 4139757568UL); | ||
| 198 | checkf ("-155,209,728", "%-'d", -155209728); | ||
| 199 | checkf ("-155209728", "%.zi", (size_t) -155209728); | ||
| 200 | checkf ("-155209728", "%zi", (size_t) -155209728); | ||
| 201 | checkf ("-155209728", "%zd", (size_t) -155209728); | ||
| 202 | checkf ("-155209728", "%+zi", (size_t) -155209728); | ||
| 203 | |||
| 204 | if (failure_cnt == 0) | ||
| 205 | printf ("\nstdio: PASS\n"); | ||
| 206 | else | ||
| 207 | printf ("\nstdio: FAIL: %d tests failed\n", failure_cnt); | ||
| 208 | } | ||
diff --git a/pintos-progos/tests/internal/stdlib.c b/pintos-progos/tests/internal/stdlib.c new file mode 100644 index 0000000..ad0f0f9 --- /dev/null +++ b/pintos-progos/tests/internal/stdlib.c | |||
| @@ -0,0 +1,114 @@ | |||
| 1 | /* Test program for sorting and searching in lib/stdlib.c. | ||
| 2 | |||
| 3 | Attempts to test the sorting and searching functionality that | ||
| 4 | is not sufficiently tested elsewhere in Pintos. | ||
| 5 | |||
| 6 | This is not a test we will run on your submitted projects. | ||
| 7 | It is here for completeness. | ||
| 8 | */ | ||
| 9 | |||
| 10 | #undef NDEBUG | ||
| 11 | #include <debug.h> | ||
| 12 | #include <limits.h> | ||
| 13 | #include <random.h> | ||
| 14 | #include <stdlib.h> | ||
| 15 | #include <stdio.h> | ||
| 16 | #include "threads/test.h" | ||
| 17 | |||
| 18 | /* Maximum number of elements in an array that we will test. */ | ||
| 19 | #define MAX_CNT 4096 | ||
| 20 | |||
| 21 | static void shuffle (int[], size_t); | ||
| 22 | static int compare_ints (const void *, const void *); | ||
| 23 | static void verify_order (const int[], size_t); | ||
| 24 | static void verify_bsearch (const int[], size_t); | ||
| 25 | |||
| 26 | /* Test sorting and searching implementations. */ | ||
| 27 | void | ||
| 28 | test (void) | ||
| 29 | { | ||
| 30 | int cnt; | ||
| 31 | |||
| 32 | printf ("testing various size arrays:"); | ||
| 33 | for (cnt = 0; cnt < MAX_CNT; cnt = cnt * 4 / 3 + 1) | ||
| 34 | { | ||
| 35 | int repeat; | ||
| 36 | |||
| 37 | printf (" %zu", cnt); | ||
| 38 | for (repeat = 0; repeat < 10; repeat++) | ||
| 39 | { | ||
| 40 | static int values[MAX_CNT]; | ||
| 41 | int i; | ||
| 42 | |||
| 43 | /* Put values 0...CNT in random order in VALUES. */ | ||
| 44 | for (i = 0; i < cnt; i++) | ||
| 45 | values[i] = i; | ||
| 46 | shuffle (values, cnt); | ||
| 47 | |||
| 48 | /* Sort VALUES, then verify ordering. */ | ||
| 49 | qsort (values, cnt, sizeof *values, compare_ints); | ||
| 50 | verify_order (values, cnt); | ||
| 51 | verify_bsearch (values, cnt); | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | printf (" done\n"); | ||
| 56 | printf ("stdlib: PASS\n"); | ||
| 57 | } | ||
| 58 | |||
| 59 | /* Shuffles the CNT elements in ARRAY into random order. */ | ||
| 60 | static void | ||
| 61 | shuffle (int *array, size_t cnt) | ||
| 62 | { | ||
| 63 | size_t i; | ||
| 64 | |||
| 65 | for (i = 0; i < cnt; i++) | ||
| 66 | { | ||
| 67 | size_t j = i + random_ulong () % (cnt - i); | ||
| 68 | int t = array[j]; | ||
| 69 | array[j] = array[i]; | ||
| 70 | array[i] = t; | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | /* Returns 1 if *A is greater than *B, | ||
| 75 | 0 if *A equals *B, | ||
| 76 | -1 if *A is less than *B. */ | ||
| 77 | static int | ||
| 78 | compare_ints (const void *a_, const void *b_) | ||
| 79 | { | ||
| 80 | const int *a = a_; | ||
| 81 | const int *b = b_; | ||
| 82 | |||
| 83 | return *a < *b ? -1 : *a > *b; | ||
| 84 | } | ||
| 85 | |||
| 86 | /* Verifies that ARRAY contains the CNT ints 0...CNT-1. */ | ||
| 87 | static void | ||
| 88 | verify_order (const int *array, size_t cnt) | ||
| 89 | { | ||
| 90 | int i; | ||
| 91 | |||
| 92 | for (i = 0; (size_t) i < cnt; i++) | ||
| 93 | ASSERT (array[i] == i); | ||
| 94 | } | ||
| 95 | |||
| 96 | /* Checks that bsearch() works properly in ARRAY. ARRAY must | ||
| 97 | contain the values 0...CNT-1. */ | ||
| 98 | static void | ||
| 99 | verify_bsearch (const int *array, size_t cnt) | ||
| 100 | { | ||
| 101 | int not_in_array[] = {0, -1, INT_MAX, MAX_CNT, MAX_CNT + 1, MAX_CNT * 2}; | ||
| 102 | int i; | ||
| 103 | |||
| 104 | /* Check that all the values in the array are found properly. */ | ||
| 105 | for (i = 0; (size_t) i < cnt; i++) | ||
| 106 | ASSERT (bsearch (&i, array, cnt, sizeof *array, compare_ints) | ||
| 107 | == array + i); | ||
| 108 | |||
| 109 | /* Check that some values not in the array are not found. */ | ||
| 110 | not_in_array[0] = cnt; | ||
| 111 | for (i = 0; (size_t) i < sizeof not_in_array / sizeof *not_in_array; i++) | ||
| 112 | ASSERT (bsearch (¬_in_array[i], array, cnt, sizeof *array, compare_ints) | ||
| 113 | == NULL); | ||
| 114 | } | ||
diff --git a/pintos-progos/tests/intro/Grading b/pintos-progos/tests/intro/Grading new file mode 100644 index 0000000..b1ff6b8 --- /dev/null +++ b/pintos-progos/tests/intro/Grading | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | # Percentage of the testing point total designated for each set of | ||
| 2 | # tests. | ||
| 3 | |||
| 4 | 50.0% tests/intro/alarm-clock/Rubric | ||
| 5 | 50.0% tests/intro/userprog-args/Rubric | ||
diff --git a/pintos-progos/tests/intro/alarm-clock/Make.tests b/pintos-progos/tests/intro/alarm-clock/Make.tests new file mode 100644 index 0000000..55ad443 --- /dev/null +++ b/pintos-progos/tests/intro/alarm-clock/Make.tests | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | tests/intro/alarm-clock/%.output: SIMULATOR = bochs | ||
| 3 | tests/intro/alarm-clock/%.output: PINTOSOPTS += --kernel-test | ||
| 4 | tests/intro/alarm-clock/%.output: FILESYSSOURCE = --filesys-size=1 | ||
| 5 | |||
| 6 | # Test names. | ||
| 7 | tests/intro/alarm-clock_TESTS = $(addprefix tests/intro/alarm-clock/,alarm-single \ | ||
| 8 | alarm-multiple alarm-simultaneous alarm-zero alarm-negative) | ||
| 9 | |||
| 10 | # Sources for tests. | ||
| 11 | tests/intro/alarm-clock_SRC = tests/intro/alarm-clock/tests.c | ||
| 12 | tests/intro/alarm-clock_SRC += tests/intro/alarm-clock/alarm-wait.c | ||
| 13 | tests/intro/alarm-clock_SRC += tests/intro/alarm-clock/alarm-simultaneous.c | ||
| 14 | tests/intro/alarm-clock_SRC += tests/intro/alarm-clock/alarm-zero.c | ||
| 15 | tests/intro/alarm-clock_SRC += tests/intro/alarm-clock/alarm-negative.c | ||
diff --git a/pintos-progos/tests/intro/alarm-clock/Rubric b/pintos-progos/tests/intro/alarm-clock/Rubric new file mode 100644 index 0000000..0cf3dc1 --- /dev/null +++ b/pintos-progos/tests/intro/alarm-clock/Rubric | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | Functionality and robustness of alarm clock: | ||
| 2 | 4 alarm-single | ||
| 3 | 4 alarm-multiple | ||
| 4 | 4 alarm-simultaneous | ||
| 5 | 1 alarm-zero | ||
| 6 | 1 alarm-negative | ||
| 7 | |||
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-multiple.ck b/pintos-progos/tests/intro/alarm-clock/alarm-multiple.ck new file mode 120000 index 0000000..f3a9edc --- /dev/null +++ b/pintos-progos/tests/intro/alarm-clock/alarm-multiple.ck | |||
| @@ -0,0 +1 @@ | |||
| ../../threads/alarm-multiple.ck \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-negative.c b/pintos-progos/tests/intro/alarm-clock/alarm-negative.c new file mode 120000 index 0000000..483aa63 --- /dev/null +++ b/pintos-progos/tests/intro/alarm-clock/alarm-negative.c | |||
| @@ -0,0 +1 @@ | |||
| ../../threads/alarm-negative.c \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-negative.ck b/pintos-progos/tests/intro/alarm-clock/alarm-negative.ck new file mode 120000 index 0000000..279520e --- /dev/null +++ b/pintos-progos/tests/intro/alarm-clock/alarm-negative.ck | |||
| @@ -0,0 +1 @@ | |||
| ../../threads/alarm-negative.ck \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-simultaneous.c b/pintos-progos/tests/intro/alarm-clock/alarm-simultaneous.c new file mode 120000 index 0000000..6362b61 --- /dev/null +++ b/pintos-progos/tests/intro/alarm-clock/alarm-simultaneous.c | |||
| @@ -0,0 +1 @@ | |||
| ../../threads/alarm-simultaneous.c \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-simultaneous.ck b/pintos-progos/tests/intro/alarm-clock/alarm-simultaneous.ck new file mode 120000 index 0000000..7226d0c --- /dev/null +++ b/pintos-progos/tests/intro/alarm-clock/alarm-simultaneous.ck | |||
| @@ -0,0 +1 @@ | |||
| ../../threads/alarm-simultaneous.ck \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-single.ck b/pintos-progos/tests/intro/alarm-clock/alarm-single.ck new file mode 120000 index 0000000..7f98a51 --- /dev/null +++ b/pintos-progos/tests/intro/alarm-clock/alarm-single.ck | |||
| @@ -0,0 +1 @@ | |||
| ../../threads/alarm-single.ck \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-wait.c b/pintos-progos/tests/intro/alarm-clock/alarm-wait.c new file mode 120000 index 0000000..2755ae5 --- /dev/null +++ b/pintos-progos/tests/intro/alarm-clock/alarm-wait.c | |||
| @@ -0,0 +1 @@ | |||
| ../../threads/alarm-wait.c \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-zero.c b/pintos-progos/tests/intro/alarm-clock/alarm-zero.c new file mode 120000 index 0000000..a1f3ca7 --- /dev/null +++ b/pintos-progos/tests/intro/alarm-clock/alarm-zero.c | |||
| @@ -0,0 +1 @@ | |||
| ../../threads/alarm-zero.c \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-zero.ck b/pintos-progos/tests/intro/alarm-clock/alarm-zero.ck new file mode 120000 index 0000000..3f98d64 --- /dev/null +++ b/pintos-progos/tests/intro/alarm-clock/alarm-zero.ck | |||
| @@ -0,0 +1 @@ | |||
| ../../threads/alarm-zero.ck \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/alarm-clock/tests.c b/pintos-progos/tests/intro/alarm-clock/tests.c new file mode 100644 index 0000000..4a96360 --- /dev/null +++ b/pintos-progos/tests/intro/alarm-clock/tests.c | |||
| @@ -0,0 +1,80 @@ | |||
| 1 | #include "tests/threads/tests.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include <string.h> | ||
| 4 | #include <stdio.h> | ||
| 5 | |||
| 6 | struct test | ||
| 7 | { | ||
| 8 | const char *name; | ||
| 9 | test_func *function; | ||
| 10 | }; | ||
| 11 | |||
| 12 | static const struct test tests[] = | ||
| 13 | { | ||
| 14 | {"alarm-single", test_alarm_single}, | ||
| 15 | {"alarm-multiple", test_alarm_multiple}, | ||
| 16 | {"alarm-simultaneous", test_alarm_simultaneous}, | ||
| 17 | {"alarm-zero", test_alarm_zero}, | ||
| 18 | {"alarm-negative", test_alarm_negative}, | ||
| 19 | }; | ||
| 20 | |||
| 21 | static const char *test_name; | ||
| 22 | |||
| 23 | /* Runs the test named NAME. */ | ||
| 24 | void | ||
| 25 | run_test (const char *name) | ||
| 26 | { | ||
| 27 | const struct test *t; | ||
| 28 | |||
| 29 | for (t = tests; t < tests + sizeof tests / sizeof *tests; t++) | ||
| 30 | if (!strcmp (name, t->name)) | ||
| 31 | { | ||
| 32 | test_name = name; | ||
| 33 | msg ("begin"); | ||
| 34 | t->function (); | ||
| 35 | msg ("end"); | ||
| 36 | return; | ||
| 37 | } | ||
| 38 | PANIC ("no test named \"%s\"", name); | ||
| 39 | } | ||
| 40 | |||
| 41 | /* Prints FORMAT as if with printf(), | ||
| 42 | prefixing the output by the name of the test | ||
| 43 | and following it with a new-line character. */ | ||
| 44 | void | ||
| 45 | msg (const char *format, ...) | ||
| 46 | { | ||
| 47 | va_list args; | ||
| 48 | |||
| 49 | printf ("(%s) ", test_name); | ||
| 50 | va_start (args, format); | ||
| 51 | vprintf (format, args); | ||
| 52 | va_end (args); | ||
| 53 | putchar ('\n'); | ||
| 54 | } | ||
| 55 | |||
| 56 | /* Prints failure message FORMAT as if with printf(), | ||
| 57 | prefixing the output by the name of the test and FAIL: | ||
| 58 | and following it with a new-line character, | ||
| 59 | and then panics the kernel. */ | ||
| 60 | void | ||
| 61 | fail (const char *format, ...) | ||
| 62 | { | ||
| 63 | va_list args; | ||
| 64 | |||
| 65 | printf ("(%s) FAIL: ", test_name); | ||
| 66 | va_start (args, format); | ||
| 67 | vprintf (format, args); | ||
| 68 | va_end (args); | ||
| 69 | putchar ('\n'); | ||
| 70 | |||
| 71 | PANIC ("test failed"); | ||
| 72 | } | ||
| 73 | |||
| 74 | /* Prints a message indicating the current test passed. */ | ||
| 75 | void | ||
| 76 | pass (void) | ||
| 77 | { | ||
| 78 | printf ("(%s) PASS\n", test_name); | ||
| 79 | } | ||
| 80 | |||
diff --git a/pintos-progos/tests/intro/userprog-args/Make.tests b/pintos-progos/tests/intro/userprog-args/Make.tests new file mode 100644 index 0000000..6f7a474 --- /dev/null +++ b/pintos-progos/tests/intro/userprog-args/Make.tests | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | |||
| 3 | tests/intro/userprog-args/%.output: FILESYSSOURCE = --filesys-size=2 | ||
| 4 | tests/intro/userprog-args/%.output: PUTFILES = $(filter-out kernel.bin loader.bin, $^) | ||
| 5 | tests/intro/userprog-args/%.output: SIMULATOR = --qemu | ||
| 6 | tests/intro/userprog-args_TESTS = $(addprefix tests/intro/userprog-args/,args-none \ | ||
| 7 | args-single args-multiple args-many args-dbl-space args-limit) | ||
| 8 | |||
| 9 | tests/intro/userprog-args_PROGS = $(tests/intro/userprog-args_TESTS) $(addprefix \ | ||
| 10 | tests/intro/userprog-args/,child-simple child-args) | ||
| 11 | |||
| 12 | tests/intro/userprog-args/args-none_SRC = tests/intro/userprog-args/args.c | ||
| 13 | tests/intro/userprog-args/args-single_SRC = tests/intro/userprog-args/args.c | ||
| 14 | tests/intro/userprog-args/args-multiple_SRC = tests/intro/userprog-args/args.c | ||
| 15 | tests/intro/userprog-args/args-many_SRC = tests/intro/userprog-args/args.c | ||
| 16 | tests/intro/userprog-args/args-dbl-space_SRC = tests/intro/userprog-args/args.c | ||
| 17 | tests/intro/userprog-args/args-limit_SRC = tests/intro/userprog-args/args-limit.c | ||
| 18 | |||
| 19 | tests/intro/userprog-args/child-simple_SRC = tests/intro/userprog-args/child-simple.c | ||
| 20 | tests/intro/userprog-args/child-args_SRC = tests/intro/userprog-args/args.c | ||
| 21 | |||
| 22 | $(foreach prog,$(tests/intro/userprog-args_PROGS),$(eval $(prog)_SRC += tests/lib.c)) | ||
| 23 | |||
| 24 | tests/intro/userprog-args/args-single_ARGS = onearg | ||
| 25 | tests/intro/userprog-args/args-multiple_ARGS = some arguments for you! | ||
| 26 | tests/intro/userprog-args/args-many_ARGS = a b c d e f g h i j k l m n o p q r s t u v | ||
| 27 | tests/intro/userprog-args/args-dbl-space_ARGS = two spaces! | ||
diff --git a/pintos-progos/tests/intro/userprog-args/Rubric b/pintos-progos/tests/intro/userprog-args/Rubric new file mode 100644 index 0000000..f5474c5 --- /dev/null +++ b/pintos-progos/tests/intro/userprog-args/Rubric | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | Functionality of stack setup: | ||
| 2 | 3 args-none | ||
| 3 | 3 args-single | ||
| 4 | 3 args-multiple | ||
| 5 | 3 args-many | ||
| 6 | 3 args-dbl-space | ||
| 7 | 3 args-limit | ||
diff --git a/pintos-progos/tests/intro/userprog-args/args-dbl-space.ck b/pintos-progos/tests/intro/userprog-args/args-dbl-space.ck new file mode 120000 index 0000000..730d787 --- /dev/null +++ b/pintos-progos/tests/intro/userprog-args/args-dbl-space.ck | |||
| @@ -0,0 +1 @@ | |||
| ../../userprog/args-dbl-space.ck \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/userprog-args/args-limit.c b/pintos-progos/tests/intro/userprog-args/args-limit.c new file mode 100644 index 0000000..159d868 --- /dev/null +++ b/pintos-progos/tests/intro/userprog-args/args-limit.c | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | /* Test the limit for (1) number of arguments and (2) total size of arguments */ | ||
| 2 | #include <syscall.h> | ||
| 3 | #include <string.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | |||
| 6 | #define MAX_SIZE 4096 | ||
| 7 | |||
| 8 | static bool recurse (int, int); | ||
| 9 | |||
| 10 | char cmd[MAX_SIZE * 4]; | ||
| 11 | |||
| 12 | static bool | ||
| 13 | recurse (int argsize, int argcount) | ||
| 14 | { | ||
| 15 | int i, j; | ||
| 16 | char *p; | ||
| 17 | strlcpy (cmd, "args-limit", 11); | ||
| 18 | p = cmd+strlen(cmd); | ||
| 19 | for (i = 0; i < argcount; i++) { | ||
| 20 | *p++ = ' '; | ||
| 21 | for (j = 0; j < argsize; j++) { | ||
| 22 | *p++ = 'X'; | ||
| 23 | } | ||
| 24 | } | ||
| 25 | *p = 0; | ||
| 26 | if (wait (exec (cmd)) < 0) { | ||
| 27 | return false; | ||
| 28 | } else { | ||
| 29 | return true; | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | int | ||
| 34 | main (int argc, char **argv) | ||
| 35 | { | ||
| 36 | test_name = argv[0]; | ||
| 37 | if(argc <= 1) { | ||
| 38 | int step; | ||
| 39 | int max_args = 0, max_size = 0; | ||
| 40 | |||
| 41 | msg ("begin"); | ||
| 42 | |||
| 43 | /* Binary search number of arguments */ | ||
| 44 | for (step = MAX_SIZE; step > 0 && max_args < MAX_SIZE; step>>=1) { | ||
| 45 | int t = max_args + step; | ||
| 46 | if (recurse (1, t)) { | ||
| 47 | max_args = t; | ||
| 48 | } | ||
| 49 | } | ||
| 50 | if (max_args > 63) { | ||
| 51 | msg ("success. at least 64 command line arguments are supported."); | ||
| 52 | } else { | ||
| 53 | msg ("FAIL: Only %d command line arguments are supported",max_args); | ||
| 54 | } | ||
| 55 | /* Binary search size of arguments */ | ||
| 56 | for (step = MAX_SIZE; step > 0 && max_size < MAX_SIZE; step>>=1) { | ||
| 57 | int t = max_size + step; | ||
| 58 | if (recurse (t, 1)) { | ||
| 59 | max_size = t; | ||
| 60 | } | ||
| 61 | } | ||
| 62 | if (max_size >= 100) { | ||
| 63 | msg ("success. arguments with at least 100 bytes are supported."); | ||
| 64 | } else { | ||
| 65 | msg ("FAIL: Arguments with more than %d bytes are not supported.",max_size); | ||
| 66 | } | ||
| 67 | msg ("end"); | ||
| 68 | } | ||
| 69 | return 0; | ||
| 70 | } | ||
diff --git a/pintos-progos/tests/intro/userprog-args/args-limit.ck b/pintos-progos/tests/intro/userprog-args/args-limit.ck new file mode 100644 index 0000000..ed8ead2 --- /dev/null +++ b/pintos-progos/tests/intro/userprog-args/args-limit.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (args-limit) begin | ||
| 7 | (args-limit) success. at least 64 command line arguments are supported. | ||
| 8 | (args-limit) success. arguments with at least 100 bytes are supported. | ||
| 9 | (args-limit) end | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/intro/userprog-args/args-many.ck b/pintos-progos/tests/intro/userprog-args/args-many.ck new file mode 120000 index 0000000..ed175bd --- /dev/null +++ b/pintos-progos/tests/intro/userprog-args/args-many.ck | |||
| @@ -0,0 +1 @@ | |||
| ../../userprog/args-many.ck \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/userprog-args/args-multiple.ck b/pintos-progos/tests/intro/userprog-args/args-multiple.ck new file mode 120000 index 0000000..4f9935f --- /dev/null +++ b/pintos-progos/tests/intro/userprog-args/args-multiple.ck | |||
| @@ -0,0 +1 @@ | |||
| ../../userprog/args-multiple.ck \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/userprog-args/args-none.ck b/pintos-progos/tests/intro/userprog-args/args-none.ck new file mode 120000 index 0000000..861d319 --- /dev/null +++ b/pintos-progos/tests/intro/userprog-args/args-none.ck | |||
| @@ -0,0 +1 @@ | |||
| ../../userprog/args-none.ck \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/userprog-args/args-single.ck b/pintos-progos/tests/intro/userprog-args/args-single.ck new file mode 120000 index 0000000..df8e737 --- /dev/null +++ b/pintos-progos/tests/intro/userprog-args/args-single.ck | |||
| @@ -0,0 +1 @@ | |||
| ../../userprog/args-single.ck \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/userprog-args/args.c b/pintos-progos/tests/intro/userprog-args/args.c new file mode 120000 index 0000000..0d798ac --- /dev/null +++ b/pintos-progos/tests/intro/userprog-args/args.c | |||
| @@ -0,0 +1 @@ | |||
| ../../userprog/args.c \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/userprog-args/child-simple.c b/pintos-progos/tests/intro/userprog-args/child-simple.c new file mode 120000 index 0000000..f8a3ce7 --- /dev/null +++ b/pintos-progos/tests/intro/userprog-args/child-simple.c | |||
| @@ -0,0 +1 @@ | |||
| ../../userprog/child-simple.c \ No newline at end of file | |||
diff --git a/pintos-progos/tests/lib.c b/pintos-progos/tests/lib.c new file mode 100644 index 0000000..ee36505 --- /dev/null +++ b/pintos-progos/tests/lib.c | |||
| @@ -0,0 +1,196 @@ | |||
| 1 | #include "tests/lib.h" | ||
| 2 | #include <random.h> | ||
| 3 | #include <stdarg.h> | ||
| 4 | #include <stdio.h> | ||
| 5 | #include <string.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | |||
| 8 | const char *test_name; | ||
| 9 | bool quiet = false; | ||
| 10 | |||
| 11 | static void | ||
| 12 | vmsg (const char *format, va_list args, const char *suffix) | ||
| 13 | { | ||
| 14 | /* We go to some trouble to stuff the entire message into a | ||
| 15 | single buffer and output it in a single system call, because | ||
| 16 | that'll (typically) ensure that it gets sent to the console | ||
| 17 | atomically. Otherwise kernel messages like "foo: exit(0)" | ||
| 18 | can end up being interleaved if we're unlucky. */ | ||
| 19 | static char buf[1024]; | ||
| 20 | |||
| 21 | snprintf (buf, sizeof buf, "(%s) ", test_name); | ||
| 22 | vsnprintf (buf + strlen (buf), sizeof buf - strlen (buf), format, args); | ||
| 23 | strlcpy (buf + strlen (buf), suffix, sizeof buf - strlen (buf)); | ||
| 24 | write (STDOUT_FILENO, buf, strlen (buf)); | ||
| 25 | } | ||
| 26 | |||
| 27 | void | ||
| 28 | msg (const char *format, ...) | ||
| 29 | { | ||
| 30 | va_list args; | ||
| 31 | |||
| 32 | if (quiet) | ||
| 33 | return; | ||
| 34 | va_start (args, format); | ||
| 35 | vmsg (format, args, "\n"); | ||
| 36 | va_end (args); | ||
| 37 | } | ||
| 38 | |||
| 39 | void | ||
| 40 | fail (const char *format, ...) | ||
| 41 | { | ||
| 42 | va_list args; | ||
| 43 | |||
| 44 | va_start (args, format); | ||
| 45 | vmsg (format, args, ": FAILED\n"); | ||
| 46 | va_end (args); | ||
| 47 | |||
| 48 | exit (1); | ||
| 49 | } | ||
| 50 | |||
| 51 | static void | ||
| 52 | swap (void *a_, void *b_, size_t size) | ||
| 53 | { | ||
| 54 | uint8_t *a = a_; | ||
| 55 | uint8_t *b = b_; | ||
| 56 | size_t i; | ||
| 57 | |||
| 58 | for (i = 0; i < size; i++) | ||
| 59 | { | ||
| 60 | uint8_t t = a[i]; | ||
| 61 | a[i] = b[i]; | ||
| 62 | b[i] = t; | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | void | ||
| 67 | shuffle (void *buf_, size_t cnt, size_t size) | ||
| 68 | { | ||
| 69 | char *buf = buf_; | ||
| 70 | size_t i; | ||
| 71 | |||
| 72 | for (i = 0; i < cnt; i++) | ||
| 73 | { | ||
| 74 | size_t j = i + random_ulong () % (cnt - i); | ||
| 75 | swap (buf + i * size, buf + j * size, size); | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | void | ||
| 80 | exec_children (const char *child_name, pid_t pids[], size_t child_cnt) | ||
| 81 | { | ||
| 82 | size_t i; | ||
| 83 | |||
| 84 | for (i = 0; i < child_cnt; i++) | ||
| 85 | { | ||
| 86 | char cmd_line[128]; | ||
| 87 | snprintf (cmd_line, sizeof cmd_line, "%s %zu", child_name, i); | ||
| 88 | CHECK ((pids[i] = exec (cmd_line)) != PID_ERROR, | ||
| 89 | "exec child %zu of %zu: \"%s\"", i + 1, child_cnt, cmd_line); | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | void | ||
| 94 | wait_children (pid_t pids[], size_t child_cnt) | ||
| 95 | { | ||
| 96 | size_t i; | ||
| 97 | |||
| 98 | for (i = 0; i < child_cnt; i++) | ||
| 99 | { | ||
| 100 | int status = wait (pids[i]); | ||
| 101 | CHECK (status == (int) i, | ||
| 102 | "wait for child %zu of %zu returned %d (expected %zu)", | ||
| 103 | i + 1, child_cnt, status, i); | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | void | ||
| 108 | check_file_handle (int fd, | ||
| 109 | const char *file_name, const void *buf_, size_t size) | ||
| 110 | { | ||
| 111 | const char *buf = buf_; | ||
| 112 | size_t ofs = 0; | ||
| 113 | size_t file_size; | ||
| 114 | |||
| 115 | /* Warn about file of wrong size. Don't fail yet because we | ||
| 116 | may still be able to get more information by reading the | ||
| 117 | file. */ | ||
| 118 | file_size = filesize (fd); | ||
| 119 | if (file_size != size) | ||
| 120 | msg ("size of %s (%zu) differs from expected (%zu)", | ||
| 121 | file_name, file_size, size); | ||
| 122 | |||
| 123 | /* Read the file block-by-block, comparing data as we go. */ | ||
| 124 | while (ofs < size) | ||
| 125 | { | ||
| 126 | char block[512]; | ||
| 127 | size_t block_size, ret_val; | ||
| 128 | |||
| 129 | block_size = size - ofs; | ||
| 130 | if (block_size > sizeof block) | ||
| 131 | block_size = sizeof block; | ||
| 132 | |||
| 133 | ret_val = read (fd, block, block_size); | ||
| 134 | if (ret_val != block_size) | ||
| 135 | fail ("read of %zu bytes at offset %zu in \"%s\" returned %zu", | ||
| 136 | block_size, ofs, file_name, ret_val); | ||
| 137 | |||
| 138 | compare_bytes (block, buf + ofs, block_size, ofs, file_name); | ||
| 139 | ofs += block_size; | ||
| 140 | } | ||
| 141 | |||
| 142 | /* Now fail due to wrong file size. */ | ||
| 143 | if (file_size != size) | ||
| 144 | fail ("size of %s (%zu) differs from expected (%zu)", | ||
| 145 | file_name, file_size, size); | ||
| 146 | |||
| 147 | msg ("verified contents of \"%s\"", file_name); | ||
| 148 | } | ||
| 149 | |||
| 150 | void | ||
| 151 | check_file (const char *file_name, const void *buf, size_t size) | ||
| 152 | { | ||
| 153 | int fd; | ||
| 154 | |||
| 155 | CHECK ((fd = open (file_name)) > 1, "open \"%s\" for verification", | ||
| 156 | file_name); | ||
| 157 | check_file_handle (fd, file_name, buf, size); | ||
| 158 | msg ("close \"%s\"", file_name); | ||
| 159 | close (fd); | ||
| 160 | } | ||
| 161 | |||
| 162 | void | ||
| 163 | compare_bytes (const void *read_data_, const void *expected_data_, size_t size, | ||
| 164 | size_t ofs, const char *file_name) | ||
| 165 | { | ||
| 166 | const uint8_t *read_data = read_data_; | ||
| 167 | const uint8_t *expected_data = expected_data_; | ||
| 168 | size_t i, j; | ||
| 169 | size_t show_cnt; | ||
| 170 | |||
| 171 | if (!memcmp (read_data, expected_data, size)) | ||
| 172 | return; | ||
| 173 | |||
| 174 | for (i = 0; i < size; i++) | ||
| 175 | if (read_data[i] != expected_data[i]) | ||
| 176 | break; | ||
| 177 | for (j = i + 1; j < size; j++) | ||
| 178 | if (read_data[j] == expected_data[j]) | ||
| 179 | break; | ||
| 180 | |||
| 181 | quiet = false; | ||
| 182 | msg ("%zu bytes read starting at offset %zu in \"%s\" differ " | ||
| 183 | "from expected.", j - i, ofs + i, file_name); | ||
| 184 | show_cnt = j - i; | ||
| 185 | if (j - i > 64) | ||
| 186 | { | ||
| 187 | show_cnt = 64; | ||
| 188 | msg ("Showing first differing %zu bytes.", show_cnt); | ||
| 189 | } | ||
| 190 | msg ("Data actually read:"); | ||
| 191 | hex_dump (ofs + i, read_data + i, show_cnt, true); | ||
| 192 | msg ("Expected data:"); | ||
| 193 | hex_dump (ofs + i, expected_data + i, show_cnt, true); | ||
| 194 | fail ("%zu bytes read starting at offset %zu in \"%s\" differ " | ||
| 195 | "from expected", j - i, ofs + i, file_name); | ||
| 196 | } | ||
diff --git a/pintos-progos/tests/lib.h b/pintos-progos/tests/lib.h new file mode 100644 index 0000000..648327b --- /dev/null +++ b/pintos-progos/tests/lib.h | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | #ifndef TESTS_LIB_H | ||
| 2 | #define TESTS_LIB_H | ||
| 3 | |||
| 4 | #include <debug.h> | ||
| 5 | #include <stdbool.h> | ||
| 6 | #include <stddef.h> | ||
| 7 | #include <syscall.h> | ||
| 8 | |||
| 9 | extern const char *test_name; | ||
| 10 | extern bool quiet; | ||
| 11 | |||
| 12 | void msg (const char *, ...) PRINTF_FORMAT (1, 2); | ||
| 13 | void fail (const char *, ...) PRINTF_FORMAT (1, 2) NO_RETURN; | ||
| 14 | |||
| 15 | /* Takes an expression to test for SUCCESS and a message, which | ||
| 16 | may include printf-style arguments. Logs the message, then | ||
| 17 | tests the expression. If it is zero, indicating failure, | ||
| 18 | emits the message as a failure. | ||
| 19 | |||
| 20 | Somewhat tricky to use: | ||
| 21 | |||
| 22 | - SUCCESS must not have side effects that affect the | ||
| 23 | message, because that will cause the original message and | ||
| 24 | the failure message to differ. | ||
| 25 | |||
| 26 | - The message must not have side effects of its own, because | ||
| 27 | it will be printed twice on failure, or zero times on | ||
| 28 | success if quiet is set. */ | ||
| 29 | #define CHECK(SUCCESS, ...) \ | ||
| 30 | do \ | ||
| 31 | { \ | ||
| 32 | msg (__VA_ARGS__); \ | ||
| 33 | if (!(SUCCESS)) \ | ||
| 34 | fail (__VA_ARGS__); \ | ||
| 35 | } \ | ||
| 36 | while (0) | ||
| 37 | |||
| 38 | void shuffle (void *, size_t cnt, size_t size); | ||
| 39 | |||
| 40 | void exec_children (const char *child_name, pid_t pids[], size_t child_cnt); | ||
| 41 | void wait_children (pid_t pids[], size_t child_cnt); | ||
| 42 | |||
| 43 | void check_file_handle (int fd, const char *file_name, | ||
| 44 | const void *buf_, size_t filesize); | ||
| 45 | void check_file (const char *file_name, const void *buf, size_t filesize); | ||
| 46 | |||
| 47 | void compare_bytes (const void *read_data, const void *expected_data, | ||
| 48 | size_t size, size_t ofs, const char *file_name); | ||
| 49 | |||
| 50 | #endif /* test/lib.h */ | ||
diff --git a/pintos-progos/tests/lib.pm b/pintos-progos/tests/lib.pm new file mode 100644 index 0000000..bc37ae5 --- /dev/null +++ b/pintos-progos/tests/lib.pm | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | use strict; | ||
| 2 | use warnings; | ||
| 3 | |||
| 4 | use tests::random; | ||
| 5 | |||
| 6 | sub shuffle { | ||
| 7 | my ($in, $cnt, $sz) = @_; | ||
| 8 | $cnt * $sz == length $in or die; | ||
| 9 | my (@a) = 0...$cnt - 1; | ||
| 10 | for my $i (0...$cnt - 1) { | ||
| 11 | my ($j) = $i + random_ulong () % ($cnt - $i); | ||
| 12 | @a[$i, $j] = @a[$j, $i]; | ||
| 13 | } | ||
| 14 | my ($out) = ""; | ||
| 15 | $out .= substr ($in, $_ * $sz, $sz) foreach @a; | ||
| 16 | return $out; | ||
| 17 | } | ||
| 18 | |||
| 19 | 1; | ||
diff --git a/pintos-progos/tests/main.c b/pintos-progos/tests/main.c new file mode 100644 index 0000000..ad1b0f1 --- /dev/null +++ b/pintos-progos/tests/main.c | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | #include <random.h> | ||
| 2 | #include "tests/lib.h" | ||
| 3 | #include "tests/main.h" | ||
| 4 | |||
| 5 | int | ||
| 6 | main (int argc UNUSED, char *argv[]) | ||
| 7 | { | ||
| 8 | test_name = argv[0]; | ||
| 9 | |||
| 10 | msg ("begin"); | ||
| 11 | random_init (0); | ||
| 12 | test_main (); | ||
| 13 | msg ("end"); | ||
| 14 | return 0; | ||
| 15 | } | ||
diff --git a/pintos-progos/tests/main.h b/pintos-progos/tests/main.h new file mode 100644 index 0000000..f0e8818 --- /dev/null +++ b/pintos-progos/tests/main.h | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | #ifndef TESTS_MAIN_H | ||
| 2 | #define TESTS_MAIN_H | ||
| 3 | |||
| 4 | void test_main (void); | ||
| 5 | |||
| 6 | #endif /* tests/main.h */ | ||
diff --git a/pintos-progos/tests/make-grade b/pintos-progos/tests/make-grade new file mode 100755 index 0000000..a3faa0e --- /dev/null +++ b/pintos-progos/tests/make-grade | |||
| @@ -0,0 +1,152 @@ | |||
| 1 | #! /usr/bin/perl | ||
| 2 | |||
| 3 | use strict; | ||
| 4 | use warnings; | ||
| 5 | |||
| 6 | @ARGV == 3 || die; | ||
| 7 | my ($src_dir, $results_file, $grading_file) = @ARGV; | ||
| 8 | |||
| 9 | # Read pass/file verdicts from $results_file. | ||
| 10 | open (RESULTS, '<', $results_file) || die "$results_file: open: $!\n"; | ||
| 11 | my (%verdicts, %verdict_counts); | ||
| 12 | while (<RESULTS>) { | ||
| 13 | my ($verdict, $test) = /^(pass|FAIL) (.*)$/ or die; | ||
| 14 | $verdicts{$test} = $verdict eq 'pass'; | ||
| 15 | } | ||
| 16 | close RESULTS; | ||
| 17 | |||
| 18 | my (@failures); | ||
| 19 | my (@overall, @rubrics, @summary); | ||
| 20 | my ($pct_actual, $pct_possible) = (0, 0); | ||
| 21 | |||
| 22 | # Read grading file. | ||
| 23 | my (@items); | ||
| 24 | open (GRADING, '<', $grading_file) || die "$grading_file: open: $!\n"; | ||
| 25 | while (<GRADING>) { | ||
| 26 | s/#.*//; | ||
| 27 | next if /^\s*$/; | ||
| 28 | my ($max_pct, $rubric_suffix) = /^\s*(\d+(?:\.\d+)?)%\t(.*)/ or die; | ||
| 29 | my ($dir) = $rubric_suffix =~ /^(.*)\//; | ||
| 30 | my ($rubric_file) = "$src_dir/$rubric_suffix"; | ||
| 31 | open (RUBRIC, '<', $rubric_file) or die "$rubric_file: open: $!\n"; | ||
| 32 | |||
| 33 | # Rubric file must begin with title line. | ||
| 34 | my $title = <RUBRIC>; | ||
| 35 | chomp $title; | ||
| 36 | $title =~ s/:$// or die; | ||
| 37 | $title .= " ($rubric_suffix):"; | ||
| 38 | push (@rubrics, $title); | ||
| 39 | |||
| 40 | my ($score, $possible) = (0, 0); | ||
| 41 | my ($cnt, $passed) = (0, 0); | ||
| 42 | my ($was_score) = 0; | ||
| 43 | while (<RUBRIC>) { | ||
| 44 | chomp; | ||
| 45 | push (@rubrics, "\t$_"), next if /^-/; | ||
| 46 | push (@rubrics, ""), next if /^\s*$/; | ||
| 47 | my ($poss, $name) = /^(\d+)\t(.*)$/ or die; | ||
| 48 | my ($test) = "$dir/$name"; | ||
| 49 | my ($points) = 0; | ||
| 50 | if (!defined $verdicts{$test}) { | ||
| 51 | push (@overall, "warning: $test not tested, assuming failure"); | ||
| 52 | } elsif ($verdicts{$test}) { | ||
| 53 | $points = $poss; | ||
| 54 | $passed++; | ||
| 55 | } | ||
| 56 | push (@failures, $test) if !$points; | ||
| 57 | $verdict_counts{$test}++; | ||
| 58 | push (@rubrics, sprintf ("\t%4s%2d/%2d %s", | ||
| 59 | $points ? '' : '**', $points, $poss, $test)); | ||
| 60 | $score += $points; | ||
| 61 | $possible += $poss; | ||
| 62 | $cnt++; | ||
| 63 | } | ||
| 64 | close (RUBRIC); | ||
| 65 | |||
| 66 | push (@rubrics, ""); | ||
| 67 | push (@rubrics, "\t- Section summary."); | ||
| 68 | push (@rubrics, sprintf ("\t%4s%3d/%3d %s", | ||
| 69 | '', $passed, $cnt, 'tests passed')); | ||
| 70 | push (@rubrics, sprintf ("\t%4s%3d/%3d %s", | ||
| 71 | '', $score, $possible, 'points subtotal')); | ||
| 72 | push (@rubrics, ''); | ||
| 73 | |||
| 74 | my ($pct) = ($score / $possible) * $max_pct; | ||
| 75 | push (@summary, sprintf ("%-45s %3d/%3d %5.1f%%/%5.1f%%", | ||
| 76 | $rubric_suffix, | ||
| 77 | $score, $possible, | ||
| 78 | $pct, $max_pct)); | ||
| 79 | $pct_actual += $pct; | ||
| 80 | $pct_possible += $max_pct; | ||
| 81 | } | ||
| 82 | close GRADING; | ||
| 83 | |||
| 84 | my ($sum_line) | ||
| 85 | = "--------------------------------------------- --- --- ------ ------"; | ||
| 86 | unshift (@summary, | ||
| 87 | "SUMMARY BY TEST SET", | ||
| 88 | '', | ||
| 89 | sprintf ("%-45s %3s %3s %6s %6s", | ||
| 90 | "Test Set", "Pts", "Max", "% Ttl", "% Max"), | ||
| 91 | $sum_line); | ||
| 92 | push (@summary, | ||
| 93 | $sum_line, | ||
| 94 | sprintf ("%-45s %3s %3s %5.1f%%/%5.1f%%", | ||
| 95 | 'Total', '', '', $pct_actual, $pct_possible)); | ||
| 96 | |||
| 97 | unshift (@rubrics, | ||
| 98 | "SUMMARY OF INDIVIDUAL TESTS", | ||
| 99 | ''); | ||
| 100 | |||
| 101 | foreach my $name (keys (%verdicts)) { | ||
| 102 | my ($count) = $verdict_counts{$name}; | ||
| 103 | if (!defined ($count) || $count != 1) { | ||
| 104 | if (!defined ($count) || !$count) { | ||
| 105 | push (@overall, "warning: test $name doesn't count for grading"); | ||
| 106 | } else { | ||
| 107 | push (@overall, | ||
| 108 | "warning: test $name counted $count times in grading"); | ||
| 109 | } | ||
| 110 | } | ||
| 111 | } | ||
| 112 | push (@overall, sprintf ("TOTAL TESTING SCORE: %.1f%%", $pct_actual)); | ||
| 113 | if (sprintf ("%.1f", $pct_actual) eq sprintf ("%.1f", $pct_possible)) { | ||
| 114 | push (@overall, "ALL TESTED PASSED -- PERFECT SCORE"); | ||
| 115 | } | ||
| 116 | |||
| 117 | my (@divider) = ('', '- ' x 38, ''); | ||
| 118 | |||
| 119 | print map ("$_\n", @overall, @divider, @summary, @divider, @rubrics); | ||
| 120 | |||
| 121 | for my $test (@failures) { | ||
| 122 | print map ("$_\n", @divider); | ||
| 123 | print "DETAILS OF $test FAILURE:\n\n"; | ||
| 124 | |||
| 125 | if (open (RESULT, '<', "$test.result")) { | ||
| 126 | my $first_line = <RESULT>; | ||
| 127 | my ($cnt) = 0; | ||
| 128 | while (<RESULT>) { | ||
| 129 | print; | ||
| 130 | $cnt++; | ||
| 131 | } | ||
| 132 | close (RESULT); | ||
| 133 | } | ||
| 134 | |||
| 135 | if (open (OUTPUT, '<', "$test.output")) { | ||
| 136 | print "\nOUTPUT FROM $test:\n\n"; | ||
| 137 | |||
| 138 | my ($panics, $boots) = (0, 0); | ||
| 139 | while (<OUTPUT>) { | ||
| 140 | if (/PANIC/ && ++$panics > 2) { | ||
| 141 | print "[...details of additional panic(s) omitted...]\n"; | ||
| 142 | last; | ||
| 143 | } | ||
| 144 | print; | ||
| 145 | if (/Pintos booting/ && ++$boots > 1) { | ||
| 146 | print "[...details of reboot(s) omitted...]\n"; | ||
| 147 | last; | ||
| 148 | } | ||
| 149 | } | ||
| 150 | close (OUTPUT); | ||
| 151 | } | ||
| 152 | } | ||
diff --git a/pintos-progos/tests/random.pm b/pintos-progos/tests/random.pm new file mode 100644 index 0000000..be008ff --- /dev/null +++ b/pintos-progos/tests/random.pm | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | use strict; | ||
| 2 | use warnings; | ||
| 3 | |||
| 4 | use tests::arc4; | ||
| 5 | |||
| 6 | my (@arc4); | ||
| 7 | |||
| 8 | sub random_init { | ||
| 9 | if (@arc4 == 0) { | ||
| 10 | my ($seed) = @_; | ||
| 11 | $seed = 0 if !defined $seed; | ||
| 12 | @arc4 = arc4_init (pack ("V", $seed)); | ||
| 13 | } | ||
| 14 | } | ||
| 15 | |||
| 16 | sub random_bytes { | ||
| 17 | random_init (); | ||
| 18 | my ($n) = @_; | ||
| 19 | return arc4_crypt (\@arc4, "\0" x $n); | ||
| 20 | } | ||
| 21 | |||
| 22 | sub random_ulong { | ||
| 23 | random_init (); | ||
| 24 | return unpack ("V", random_bytes (4)); | ||
| 25 | } | ||
| 26 | |||
| 27 | 1; | ||
diff --git a/pintos-progos/tests/tests.pm b/pintos-progos/tests/tests.pm new file mode 100644 index 0000000..4599cb9 --- /dev/null +++ b/pintos-progos/tests/tests.pm | |||
| @@ -0,0 +1,625 @@ | |||
| 1 | use strict; | ||
| 2 | use warnings; | ||
| 3 | use tests::Algorithm::Diff; | ||
| 4 | use File::Temp 'tempfile'; | ||
| 5 | use Fcntl qw(SEEK_SET SEEK_CUR); | ||
| 6 | |||
| 7 | sub fail; | ||
| 8 | sub pass; | ||
| 9 | |||
| 10 | die if @ARGV != 2; | ||
| 11 | our ($test, $src_dir) = @ARGV; | ||
| 12 | |||
| 13 | my ($msg_file) = tempfile (); | ||
| 14 | select ($msg_file); | ||
| 15 | |||
| 16 | our (@prereq_tests) = (); | ||
| 17 | if ($test =~ /^(.*)-persistence$/) { | ||
| 18 | push (@prereq_tests, $1); | ||
| 19 | } | ||
| 20 | for my $prereq_test (@prereq_tests) { | ||
| 21 | my (@result) = read_text_file ("$prereq_test.result"); | ||
| 22 | fail "Prerequisite test $prereq_test failed.\n" if $result[0] ne 'PASS'; | ||
| 23 | } | ||
| 24 | |||
| 25 | |||
| 26 | # Generic testing. | ||
| 27 | |||
| 28 | sub check_expected { | ||
| 29 | my ($expected) = pop @_; | ||
| 30 | my (@options) = @_; | ||
| 31 | my (@output) = read_text_file ("$test.output"); | ||
| 32 | common_checks ("run", @output); | ||
| 33 | compare_output ("run", @options, \@output, $expected); | ||
| 34 | } | ||
| 35 | |||
| 36 | sub common_checks { | ||
| 37 | my ($run, @output) = @_; | ||
| 38 | |||
| 39 | fail "\u$run produced no output at all\n" if @output == 0; | ||
| 40 | |||
| 41 | check_for_panic ($run, @output); | ||
| 42 | check_for_keyword ($run, "FAIL", @output); | ||
| 43 | check_for_triple_fault ($run, @output); | ||
| 44 | check_for_keyword ($run, "TIMEOUT", @output); | ||
| 45 | |||
| 46 | fail "\u$run didn't start up properly: no \"Pintos booting\" message\n" | ||
| 47 | if !grep (/Pintos booting with.*kB RAM\.\.\./, @output); | ||
| 48 | fail "\u$run didn't start up properly: no \"Boot complete\" message\n" | ||
| 49 | if !grep (/Boot complete/, @output); | ||
| 50 | fail "\u$run didn't shut down properly: no \"Timer: # ticks\" message\n" | ||
| 51 | if !grep (/Timer: \d+ ticks/, @output); | ||
| 52 | fail "\u$run didn't shut down properly: no \"Powering off\" message\n" | ||
| 53 | if !grep (/Powering off/, @output); | ||
| 54 | } | ||
| 55 | |||
| 56 | sub check_for_panic { | ||
| 57 | my ($run, @output) = @_; | ||
| 58 | |||
| 59 | my ($panic) = grep (/PANIC/, @output); | ||
| 60 | return unless defined $panic; | ||
| 61 | |||
| 62 | print "Kernel panic in $run: ", substr ($panic, index ($panic, "PANIC")), | ||
| 63 | "\n"; | ||
| 64 | |||
| 65 | my (@stack_line) = grep (/Call stack:/, @output); | ||
| 66 | if (@stack_line != 0) { | ||
| 67 | my ($addrs) = $stack_line[0] =~ /Call stack:((?: 0x[0-9a-f]+)+)/; | ||
| 68 | |||
| 69 | # Find a user program to translate user virtual addresses. | ||
| 70 | my ($userprog) = ""; | ||
| 71 | $userprog = "$test" | ||
| 72 | if grep (hex ($_) < 0xc0000000, split (' ', $addrs)) > 0 && -e $test; | ||
| 73 | |||
| 74 | # Get and print the backtrace. | ||
| 75 | my ($trace) = scalar (`backtrace kernel.o $userprog $addrs`); | ||
| 76 | print "Call stack:$addrs\n"; | ||
| 77 | print "Translation of call stack:\n"; | ||
| 78 | print $trace; | ||
| 79 | |||
| 80 | # Print disclaimer. | ||
| 81 | if ($userprog ne '' && index ($trace, $userprog) >= 0) { | ||
| 82 | print <<EOF; | ||
| 83 | Translations of user virtual addresses above are based on a guess at | ||
| 84 | the binary to use. If this guess is incorrect, then those | ||
| 85 | translations will be misleading. | ||
| 86 | EOF | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | if ($panic =~ /sec_no \< d-\>capacity/) { | ||
| 91 | print <<EOF; | ||
| 92 | \nThis assertion commonly fails when accessing a file via an inode that | ||
| 93 | has been closed and freed. Freeing an inode clears all its sector | ||
| 94 | indexes to 0xcccccccc, which is not a valid sector number for disks | ||
| 95 | smaller than about 1.6 TB. | ||
| 96 | EOF | ||
| 97 | } | ||
| 98 | |||
| 99 | fail; | ||
| 100 | } | ||
| 101 | |||
| 102 | sub check_for_keyword { | ||
| 103 | my ($run, $keyword, @output) = @_; | ||
| 104 | |||
| 105 | my ($kw_line) = grep (/$keyword/, @output); | ||
| 106 | return unless defined $kw_line; | ||
| 107 | |||
| 108 | # Most output lines are prefixed by (test-name). Eliminate this | ||
| 109 | # from our message for brevity. | ||
| 110 | $kw_line =~ s/^\([^\)]+\)\s+//; | ||
| 111 | print "$run: $kw_line\n"; | ||
| 112 | |||
| 113 | fail; | ||
| 114 | } | ||
| 115 | |||
| 116 | sub check_for_triple_fault { | ||
| 117 | my ($run, @output) = @_; | ||
| 118 | |||
| 119 | my ($reboots) = grep (/Pintos booting/, @output) - 1; | ||
| 120 | return unless $reboots > 0; | ||
| 121 | |||
| 122 | print <<EOF; | ||
| 123 | \u$run spontaneously rebooted $reboots times. | ||
| 124 | This is most often caused by unhandled page faults. | ||
| 125 | Read the Triple Faults section in the Debugging chapter | ||
| 126 | of the Pintos manual for more information. | ||
| 127 | EOF | ||
| 128 | |||
| 129 | fail; | ||
| 130 | } | ||
| 131 | |||
| 132 | # Get @output without header or trailer. | ||
| 133 | sub get_core_output { | ||
| 134 | my ($run, @output) = @_; | ||
| 135 | my ($p); | ||
| 136 | |||
| 137 | my ($process); | ||
| 138 | my ($start); | ||
| 139 | for my $i (0...$#_) { | ||
| 140 | $start = $i + 1, last | ||
| 141 | if ($process) = $output[$i] =~ /^Executing '(\S+).*':$/; | ||
| 142 | } | ||
| 143 | |||
| 144 | my ($end); | ||
| 145 | for my $i ($start...$#output) { | ||
| 146 | $end = $i - 1, last if $output[$i] =~ /^Execution of '.*' complete.$/; | ||
| 147 | } | ||
| 148 | |||
| 149 | fail "\u$run didn't start a thread or process\n" if !defined $start; | ||
| 150 | fail "\u$run started '$process' but it never finished\n" if !defined $end; | ||
| 151 | |||
| 152 | return @output[$start...$end]; | ||
| 153 | } | ||
| 154 | |||
| 155 | sub compare_output { | ||
| 156 | my ($run) = shift @_; | ||
| 157 | my ($expected) = pop @_; | ||
| 158 | my ($output) = pop @_; | ||
| 159 | my (%options) = @_; | ||
| 160 | |||
| 161 | my (@output) = get_core_output ($run, @$output); | ||
| 162 | fail "\u$run didn't produce any output" if !@output; | ||
| 163 | |||
| 164 | my $ignore_exit_codes = exists $options{IGNORE_EXIT_CODES}; | ||
| 165 | if ($ignore_exit_codes) { | ||
| 166 | delete $options{IGNORE_EXIT_CODES}; | ||
| 167 | @output = grep (!/^[a-zA-Z0-9-_]+: exit\(\-?\d+\)$/, @output); | ||
| 168 | } | ||
| 169 | my $ignore_user_faults = exists $options{IGNORE_USER_FAULTS}; | ||
| 170 | if ($ignore_user_faults) { | ||
| 171 | delete $options{IGNORE_USER_FAULTS}; | ||
| 172 | @output = grep (!/^Page fault at.*in user context\.$/ | ||
| 173 | && !/: dying due to interrupt 0x0e \(.*\).$/ | ||
| 174 | && !/^Interrupt 0x0e \(.*\) at eip=/ | ||
| 175 | && !/^ cr2=.* error=.*/ | ||
| 176 | && !/^ eax=.* ebx=.* ecx=.* edx=.*/ | ||
| 177 | && !/^ esi=.* edi=.* esp=.* ebp=.*/ | ||
| 178 | && !/^ cs=.* ds=.* es=.* ss=.*/, @output); | ||
| 179 | } | ||
| 180 | die "unknown option " . (keys (%options))[0] . "\n" if %options; | ||
| 181 | |||
| 182 | my ($msg); | ||
| 183 | |||
| 184 | # Compare actual output against each allowed output. | ||
| 185 | if (ref ($expected) eq 'ARRAY') { | ||
| 186 | my ($i) = 0; | ||
| 187 | $expected = {map ((++$i => $_), @$expected)}; | ||
| 188 | } | ||
| 189 | foreach my $key (keys %$expected) { | ||
| 190 | my (@expected) = split ("\n", $expected->{$key}); | ||
| 191 | |||
| 192 | $msg .= "Acceptable output:\n"; | ||
| 193 | $msg .= join ('', map (" $_\n", @expected)); | ||
| 194 | |||
| 195 | # Check whether actual and expected match. | ||
| 196 | # If it's a perfect match, we're done. | ||
| 197 | if ($#output == $#expected) { | ||
| 198 | my ($eq) = 1; | ||
| 199 | for (my ($i) = 0; $i <= $#expected; $i++) { | ||
| 200 | $eq = 0 if $output[$i] ne $expected[$i]; | ||
| 201 | } | ||
| 202 | return $key if $eq; | ||
| 203 | } | ||
| 204 | |||
| 205 | # They differ. Output a diff. | ||
| 206 | my (@diff) = ""; | ||
| 207 | my ($d) = Algorithm::Diff->new (\@expected, \@output); | ||
| 208 | while ($d->Next ()) { | ||
| 209 | my ($ef, $el, $af, $al) = $d->Get (qw (min1 max1 min2 max2)); | ||
| 210 | if ($d->Same ()) { | ||
| 211 | push (@diff, map (" $_\n", $d->Items (1))); | ||
| 212 | } else { | ||
| 213 | push (@diff, map ("- $_\n", $d->Items (1))) if $d->Items (1); | ||
| 214 | push (@diff, map ("+ $_\n", $d->Items (2))) if $d->Items (2); | ||
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 218 | $msg .= "Differences in `diff -u' format:\n"; | ||
| 219 | $msg .= join ('', @diff); | ||
| 220 | } | ||
| 221 | |||
| 222 | # Failed to match. Report failure. | ||
| 223 | $msg .= "\n(Process exit codes are excluded for matching purposes.)\n" | ||
| 224 | if $ignore_exit_codes; | ||
| 225 | $msg .= "\n(User fault messages are excluded for matching purposes.)\n" | ||
| 226 | if $ignore_user_faults; | ||
| 227 | fail "Test output failed to match any acceptable form.\n\n$msg"; | ||
| 228 | } | ||
| 229 | |||
| 230 | # File system extraction. | ||
| 231 | |||
| 232 | # check_archive (\%CONTENTS) | ||
| 233 | # | ||
| 234 | # Checks that the extracted file system's contents match \%CONTENTS. | ||
| 235 | # Each key in the hash is a file name. Each value may be: | ||
| 236 | # | ||
| 237 | # - $FILE: Name of a host file containing the expected contents. | ||
| 238 | # | ||
| 239 | # - [$FILE, $OFFSET, $LENGTH]: An excerpt of host file $FILE | ||
| 240 | # comprising the $LENGTH bytes starting at $OFFSET. | ||
| 241 | # | ||
| 242 | # - [$CONTENTS]: The literal expected file contents, as a string. | ||
| 243 | # | ||
| 244 | # - {SUBDIR}: A subdirectory, in the same form described here, | ||
| 245 | # recursively. | ||
| 246 | sub check_archive { | ||
| 247 | my ($expected_hier) = @_; | ||
| 248 | |||
| 249 | my (@output) = read_text_file ("$test.output"); | ||
| 250 | common_checks ("file system extraction run", @output); | ||
| 251 | |||
| 252 | @output = get_core_output ("file system extraction run", @output); | ||
| 253 | @output = grep (!/^[a-zA-Z0-9-_]+: exit\(\d+\)$/, @output); | ||
| 254 | fail join ("\n", "Error extracting file system:", @output) if @output; | ||
| 255 | |||
| 256 | my ($test_base_name) = $test; | ||
| 257 | $test_base_name =~ s%.*/%%; | ||
| 258 | $test_base_name =~ s%-persistence$%%; | ||
| 259 | $expected_hier->{$test_base_name} = $prereq_tests[0]; | ||
| 260 | $expected_hier->{'tar'} = 'tests/filesys/extended/tar'; | ||
| 261 | |||
| 262 | my (%expected) = normalize_fs (flatten_hierarchy ($expected_hier, "")); | ||
| 263 | my (%actual) = read_tar ("$prereq_tests[0].tar"); | ||
| 264 | |||
| 265 | my ($errors) = 0; | ||
| 266 | foreach my $name (sort keys %expected) { | ||
| 267 | if (exists $actual{$name}) { | ||
| 268 | if (is_dir ($actual{$name}) && !is_dir ($expected{$name})) { | ||
| 269 | print "$name is a directory but should be an ordinary file.\n"; | ||
| 270 | $errors++; | ||
| 271 | } elsif (!is_dir ($actual{$name}) && is_dir ($expected{$name})) { | ||
| 272 | print "$name is an ordinary file but should be a directory.\n"; | ||
| 273 | $errors++; | ||
| 274 | } | ||
| 275 | } else { | ||
| 276 | print "$name is missing from the file system.\n"; | ||
| 277 | $errors++; | ||
| 278 | } | ||
| 279 | } | ||
| 280 | foreach my $name (sort keys %actual) { | ||
| 281 | if (!exists $expected{$name}) { | ||
| 282 | if ($name =~ /^[[:print:]]+$/) { | ||
| 283 | print "$name exists in the file system but it should not.\n"; | ||
| 284 | } else { | ||
| 285 | my ($esc_name) = $name; | ||
| 286 | $esc_name =~ s/[^[:print:]]/./g; | ||
| 287 | print <<EOF; | ||
| 288 | $esc_name exists in the file system but should not. (The name | ||
| 289 | of this file contains unusual characters that were printed as `.'.) | ||
| 290 | EOF | ||
| 291 | } | ||
| 292 | $errors++; | ||
| 293 | } | ||
| 294 | } | ||
| 295 | if ($errors) { | ||
| 296 | print "\nActual contents of file system:\n"; | ||
| 297 | print_fs (%actual); | ||
| 298 | print "\nExpected contents of file system:\n"; | ||
| 299 | print_fs (%expected); | ||
| 300 | } else { | ||
| 301 | foreach my $name (sort keys %expected) { | ||
| 302 | if (!is_dir ($expected{$name})) { | ||
| 303 | my ($exp_file, $exp_length) = open_file ($expected{$name}); | ||
| 304 | my ($act_file, $act_length) = open_file ($actual{$name}); | ||
| 305 | $errors += !compare_files ($exp_file, $exp_length, | ||
| 306 | $act_file, $act_length, $name, | ||
| 307 | !$errors); | ||
| 308 | close ($exp_file); | ||
| 309 | close ($act_file); | ||
| 310 | } | ||
| 311 | } | ||
| 312 | } | ||
| 313 | fail "Extracted file system contents are not correct.\n" if $errors; | ||
| 314 | } | ||
| 315 | |||
| 316 | # open_file ([$FILE, $OFFSET, $LENGTH]) | ||
| 317 | # open_file ([$CONTENTS]) | ||
| 318 | # | ||
| 319 | # Opens a file for the contents passed in, which must be in one of | ||
| 320 | # the two above forms that correspond to check_archive() arguments. | ||
| 321 | # | ||
| 322 | # Returns ($HANDLE, $LENGTH), where $HANDLE is the file's handle and | ||
| 323 | # $LENGTH is the number of bytes in the file's content. | ||
| 324 | sub open_file { | ||
| 325 | my ($value) = @_; | ||
| 326 | die if ref ($value) ne 'ARRAY'; | ||
| 327 | |||
| 328 | my ($file) = tempfile (); | ||
| 329 | my ($length); | ||
| 330 | if (@$value == 1) { | ||
| 331 | $length = length ($value->[0]); | ||
| 332 | $file = tempfile (); | ||
| 333 | syswrite ($file, $value->[0]) == $length | ||
| 334 | or die "writing temporary file: $!\n"; | ||
| 335 | sysseek ($file, 0, SEEK_SET); | ||
| 336 | } elsif (@$value == 3) { | ||
| 337 | $length = $value->[2]; | ||
| 338 | open ($file, '<', $value->[0]) or die "$value->[0]: open: $!\n"; | ||
| 339 | die "$value->[0]: file is smaller than expected\n" | ||
| 340 | if -s $file < $value->[1] + $length; | ||
| 341 | sysseek ($file, $value->[1], SEEK_SET); | ||
| 342 | } else { | ||
| 343 | die; | ||
| 344 | } | ||
| 345 | return ($file, $length); | ||
| 346 | } | ||
| 347 | |||
| 348 | # compare_files ($A, $A_SIZE, $B, $B_SIZE, $NAME, $VERBOSE) | ||
| 349 | # | ||
| 350 | # Compares $A_SIZE bytes in $A to $B_SIZE bytes in $B. | ||
| 351 | # ($A and $B are handles.) | ||
| 352 | # If their contents differ, prints a brief message describing | ||
| 353 | # the differences, using $NAME to identify the file. | ||
| 354 | # The message contains more detail if $VERBOSE is nonzero. | ||
| 355 | # Returns 1 if the contents are identical, 0 otherwise. | ||
| 356 | sub compare_files { | ||
| 357 | my ($a, $a_size, $b, $b_size, $name, $verbose) = @_; | ||
| 358 | my ($ofs) = 0; | ||
| 359 | select(STDOUT); | ||
| 360 | for (;;) { | ||
| 361 | my ($a_amt) = $a_size >= 1024 ? 1024 : $a_size; | ||
| 362 | my ($b_amt) = $b_size >= 1024 ? 1024 : $b_size; | ||
| 363 | my ($a_data, $b_data); | ||
| 364 | if (!defined (sysread ($a, $a_data, $a_amt)) | ||
| 365 | || !defined (sysread ($b, $b_data, $b_amt))) { | ||
| 366 | die "reading $name: $!\n"; | ||
| 367 | } | ||
| 368 | |||
| 369 | my ($a_len) = length $a_data; | ||
| 370 | my ($b_len) = length $b_data; | ||
| 371 | last if $a_len == 0 && $b_len == 0; | ||
| 372 | |||
| 373 | if ($a_data ne $b_data) { | ||
| 374 | my ($min_len) = $a_len < $b_len ? $a_len : $b_len; | ||
| 375 | my ($diff_ofs); | ||
| 376 | for ($diff_ofs = 0; $diff_ofs < $min_len; $diff_ofs++) { | ||
| 377 | last if (substr ($a_data, $diff_ofs, 1) | ||
| 378 | ne substr ($b_data, $diff_ofs, 1)); | ||
| 379 | } | ||
| 380 | |||
| 381 | printf "\nFile $name differs from expected " | ||
| 382 | . "starting at offset 0x%x.\n", $ofs + $diff_ofs; | ||
| 383 | if ($verbose ) { | ||
| 384 | print "Expected contents:\n"; | ||
| 385 | hex_dump (substr ($a_data, $diff_ofs, 64), $ofs + $diff_ofs); | ||
| 386 | print "Actual contents:\n"; | ||
| 387 | hex_dump (substr ($b_data, $diff_ofs, 64), $ofs + $diff_ofs); | ||
| 388 | } | ||
| 389 | return 0; | ||
| 390 | } | ||
| 391 | |||
| 392 | $ofs += $a_len; | ||
| 393 | $a_size -= $a_len; | ||
| 394 | $b_size -= $b_len; | ||
| 395 | } | ||
| 396 | return 1; | ||
| 397 | } | ||
| 398 | |||
| 399 | # hex_dump ($DATA, $OFS) | ||
| 400 | # | ||
| 401 | # Prints $DATA in hex and text formats. | ||
| 402 | # The first byte of $DATA corresponds to logical offset $OFS | ||
| 403 | # in whatever file the data comes from. | ||
| 404 | sub hex_dump { | ||
| 405 | my ($data, $ofs) = @_; | ||
| 406 | |||
| 407 | if ($data eq '') { | ||
| 408 | printf " (File ends at offset %08x.)\n", $ofs; | ||
| 409 | return; | ||
| 410 | } | ||
| 411 | |||
| 412 | my ($per_line) = 16; | ||
| 413 | while ((my $size = length ($data)) > 0) { | ||
| 414 | my ($start) = $ofs % $per_line; | ||
| 415 | my ($end) = $per_line; | ||
| 416 | $end = $start + $size if $end - $start > $size; | ||
| 417 | my ($n) = $end - $start; | ||
| 418 | |||
| 419 | printf "0x%08x ", int ($ofs / $per_line) * $per_line; | ||
| 420 | |||
| 421 | # Hex version. | ||
| 422 | print " " x $start; | ||
| 423 | for my $i ($start...$end - 1) { | ||
| 424 | printf "%02x", ord (substr ($data, $i - $start, 1)); | ||
| 425 | print $i == $per_line / 2 - 1 ? '-' : ' '; | ||
| 426 | } | ||
| 427 | print " " x ($per_line - $end); | ||
| 428 | |||
| 429 | # Character version. | ||
| 430 | my ($esc_data) = substr ($data, 0, $n); | ||
| 431 | $esc_data =~ s/[^[:print:]]/./g; | ||
| 432 | print "|", " " x $start, $esc_data, " " x ($per_line - $end), "|"; | ||
| 433 | |||
| 434 | print "\n"; | ||
| 435 | |||
| 436 | $data = substr ($data, $n); | ||
| 437 | $ofs += $n; | ||
| 438 | } | ||
| 439 | } | ||
| 440 | |||
| 441 | # print_fs (%FS) | ||
| 442 | # | ||
| 443 | # Prints a list of files in %FS, which must be a file system | ||
| 444 | # as flattened by flatten_hierarchy() and normalized by | ||
| 445 | # normalize_fs(). | ||
| 446 | sub print_fs { | ||
| 447 | my (%fs) = @_; | ||
| 448 | foreach my $name (sort keys %fs) { | ||
| 449 | my ($esc_name) = $name; | ||
| 450 | $esc_name =~ s/[^[:print:]]/./g; | ||
| 451 | print "$esc_name: "; | ||
| 452 | if (!is_dir ($fs{$name})) { | ||
| 453 | print +file_size ($fs{$name}), "-byte file"; | ||
| 454 | } else { | ||
| 455 | print "directory"; | ||
| 456 | } | ||
| 457 | print "\n"; | ||
| 458 | } | ||
| 459 | print "(empty)\n" if !@_; | ||
| 460 | } | ||
| 461 | |||
| 462 | # normalize_fs (%FS) | ||
| 463 | # | ||
| 464 | # Takes a file system as flattened by flatten_hierarchy(). | ||
| 465 | # Returns a similar file system in which values of the form $FILE | ||
| 466 | # are replaced by those of the form [$FILE, $OFFSET, $LENGTH]. | ||
| 467 | sub normalize_fs { | ||
| 468 | my (%fs) = @_; | ||
| 469 | foreach my $name (keys %fs) { | ||
| 470 | my ($value) = $fs{$name}; | ||
| 471 | next if is_dir ($value) || ref ($value) ne ''; | ||
| 472 | die "can't open $value\n" if !stat $value; | ||
| 473 | $fs{$name} = [$value, 0, -s _]; | ||
| 474 | } | ||
| 475 | return %fs; | ||
| 476 | } | ||
| 477 | |||
| 478 | # is_dir ($VALUE) | ||
| 479 | # | ||
| 480 | # Takes a value like one in the hash returned by flatten_hierarchy() | ||
| 481 | # and returns 1 if it represents a directory, 0 otherwise. | ||
| 482 | sub is_dir { | ||
| 483 | my ($value) = @_; | ||
| 484 | return ref ($value) eq '' && $value eq 'directory'; | ||
| 485 | } | ||
| 486 | |||
| 487 | # file_size ($VALUE) | ||
| 488 | # | ||
| 489 | # Takes a value like one in the hash returned by flatten_hierarchy() | ||
| 490 | # and returns the size of the file it represents. | ||
| 491 | sub file_size { | ||
| 492 | my ($value) = @_; | ||
| 493 | die if is_dir ($value); | ||
| 494 | die if ref ($value) ne 'ARRAY'; | ||
| 495 | return @$value > 1 ? $value->[2] : length ($value->[0]); | ||
| 496 | } | ||
| 497 | |||
| 498 | # flatten_hierarchy ($HIER_FS, $PREFIX) | ||
| 499 | # | ||
| 500 | # Takes a file system in the format expected by check_archive() and | ||
| 501 | # returns a "flattened" version in which file names include all parent | ||
| 502 | # directory names and the value of directories is just "directory". | ||
| 503 | sub flatten_hierarchy { | ||
| 504 | my (%hier_fs) = %{$_[0]}; | ||
| 505 | my ($prefix) = $_[1]; | ||
| 506 | my (%flat_fs); | ||
| 507 | for my $name (keys %hier_fs) { | ||
| 508 | my ($value) = $hier_fs{$name}; | ||
| 509 | if (ref $value eq 'HASH') { | ||
| 510 | %flat_fs = (%flat_fs, flatten_hierarchy ($value, "$prefix$name/")); | ||
| 511 | $flat_fs{"$prefix$name"} = 'directory'; | ||
| 512 | } else { | ||
| 513 | $flat_fs{"$prefix$name"} = $value; | ||
| 514 | } | ||
| 515 | } | ||
| 516 | return %flat_fs; | ||
| 517 | } | ||
| 518 | |||
| 519 | # read_tar ($ARCHIVE) | ||
| 520 | # | ||
| 521 | # Reads the ustar-format tar file in $ARCHIVE | ||
| 522 | # and returns a flattened file system for it. | ||
| 523 | sub read_tar { | ||
| 524 | my ($archive) = @_; | ||
| 525 | my (%content); | ||
| 526 | open (ARCHIVE, '<', $archive) or fail "$archive: open: $!\n"; | ||
| 527 | for (;;) { | ||
| 528 | my ($header); | ||
| 529 | if ((my $retval = sysread (ARCHIVE, $header, 512)) != 512) { | ||
| 530 | fail "$archive: unexpected end of file\n" if $retval >= 0; | ||
| 531 | fail "$archive: read: $!\n"; | ||
| 532 | } | ||
| 533 | |||
| 534 | last if $header eq "\0" x 512; | ||
| 535 | |||
| 536 | # Verify magic numbers. | ||
| 537 | if (substr ($header, 257, 6) ne "ustar\0" | ||
| 538 | || substr ($header, 263, 2) ne '00') { | ||
| 539 | fail "$archive: corrupt ustar header\n"; | ||
| 540 | } | ||
| 541 | |||
| 542 | # Verify checksum. | ||
| 543 | my ($chksum) = oct (unpack ("Z*", substr ($header, 148, 8, ' ' x 8))); | ||
| 544 | my ($correct_chksum) = unpack ("%32a*", $header); | ||
| 545 | fail "$archive: bad header checksum\n" if $chksum != $correct_chksum; | ||
| 546 | |||
| 547 | # Get file name. | ||
| 548 | my ($name) = unpack ("Z100", $header); | ||
| 549 | my ($prefix) = unpack ("Z*", substr ($header, 345)); | ||
| 550 | $name = "$prefix/$name" if $prefix ne ''; | ||
| 551 | fail "$archive: contains file with empty name" if $name eq ''; | ||
| 552 | |||
| 553 | # Get type. | ||
| 554 | my ($typeflag) = substr ($header, 156, 1); | ||
| 555 | $typeflag = '0' if $typeflag eq "\0"; | ||
| 556 | fail "unknown file type '$typeflag'\n" if $typeflag !~ /[05]/; | ||
| 557 | |||
| 558 | # Get size. | ||
| 559 | my ($size) = oct (unpack ("Z*", substr ($header, 124, 12))); | ||
| 560 | fail "bad size $size\n" if $size < 0; | ||
| 561 | $size = 0 if $typeflag eq '5'; | ||
| 562 | |||
| 563 | # Store content. | ||
| 564 | $name =~ s%^(/|\./|\.\./)*%%; # Strip leading "/", "./", "../". | ||
| 565 | $name = '' if $name eq '.' || $name eq '..'; | ||
| 566 | if (exists $content{$name}) { | ||
| 567 | fail "$archive: contains multiple entries for $name\n"; | ||
| 568 | } | ||
| 569 | if ($typeflag eq '5') { | ||
| 570 | $content{$name} = 'directory' if $name ne ''; | ||
| 571 | } else { | ||
| 572 | fail "$archive: contains file with empty name\n" if $name eq ''; | ||
| 573 | my ($position) = sysseek (ARCHIVE, 0, SEEK_CUR); | ||
| 574 | $content{$name} = [$archive, $position, $size]; | ||
| 575 | sysseek (ARCHIVE, int (($size + 511) / 512) * 512, SEEK_CUR); | ||
| 576 | } | ||
| 577 | } | ||
| 578 | close (ARCHIVE); | ||
| 579 | return %content; | ||
| 580 | } | ||
| 581 | |||
| 582 | # Utilities. | ||
| 583 | |||
| 584 | sub fail { | ||
| 585 | finish ("FAIL", @_); | ||
| 586 | } | ||
| 587 | |||
| 588 | sub pass { | ||
| 589 | finish ("PASS", @_); | ||
| 590 | } | ||
| 591 | |||
| 592 | sub finish { | ||
| 593 | my ($verdict, @messages) = @_; | ||
| 594 | |||
| 595 | seek ($msg_file, 0, 0); | ||
| 596 | push (@messages, <$msg_file>); | ||
| 597 | close ($msg_file); | ||
| 598 | chomp (@messages); | ||
| 599 | |||
| 600 | my ($result_fn) = "$test.result"; | ||
| 601 | open (RESULT, '>', $result_fn) or die "$result_fn: create: $!\n"; | ||
| 602 | print RESULT "$verdict\n"; | ||
| 603 | print RESULT "$_\n" foreach @messages; | ||
| 604 | close (RESULT); | ||
| 605 | |||
| 606 | if ($verdict eq 'PASS') { | ||
| 607 | print STDOUT "pass $test\n"; | ||
| 608 | } else { | ||
| 609 | print STDOUT "FAIL $test\n"; | ||
| 610 | } | ||
| 611 | print STDOUT "$_\n" foreach @messages; | ||
| 612 | |||
| 613 | exit 0; | ||
| 614 | } | ||
| 615 | |||
| 616 | sub read_text_file { | ||
| 617 | my ($file_name) = @_; | ||
| 618 | open (FILE, '<', $file_name) or die "$file_name: open: $!\n"; | ||
| 619 | my (@content) = <FILE>; | ||
| 620 | chomp (@content); | ||
| 621 | close (FILE); | ||
| 622 | return @content; | ||
| 623 | } | ||
| 624 | |||
| 625 | 1; | ||
diff --git a/pintos-progos/tests/threads/Grading b/pintos-progos/tests/threads/Grading new file mode 100644 index 0000000..cc235f2 --- /dev/null +++ b/pintos-progos/tests/threads/Grading | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # Percentage of the testing point total designated for each set of | ||
| 2 | # tests. | ||
| 3 | |||
| 4 | # Priority Scheduling | ||
| 5 | 90.0% tests/threads/Rubric.priority | ||
| 6 | |||
| 7 | # Robustness | ||
| 8 | 10.0% tests/threads/Rubric.alarm | ||
| 9 | |||
| 10 | # Not used in SS 2012 | ||
| 11 | # XX.0% tests/threads/Rubric.mlfqs | ||
diff --git a/pintos-progos/tests/threads/Make.tests b/pintos-progos/tests/threads/Make.tests new file mode 100644 index 0000000..dbdfd0c --- /dev/null +++ b/pintos-progos/tests/threads/Make.tests | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | |||
| 3 | # Test names. | ||
| 4 | tests/threads_TESTS = $(addprefix tests/threads/,alarm-single \ | ||
| 5 | alarm-multiple alarm-simultaneous alarm-priority alarm-zero \ | ||
| 6 | alarm-negative priority-change priority-donate-one \ | ||
| 7 | priority-donate-multiple priority-donate-multiple2 \ | ||
| 8 | priority-donate-nest priority-donate-sema priority-donate-lower \ | ||
| 9 | priority-fifo priority-preempt priority-sema priority-condvar \ | ||
| 10 | priority-donate-chain) | ||
| 11 | |||
| 12 | |||
| 13 | # Sources for tests. | ||
| 14 | tests/threads_SRC = tests/threads/tests.c | ||
| 15 | tests/threads_SRC += tests/threads/alarm-wait.c | ||
| 16 | tests/threads_SRC += tests/threads/alarm-simultaneous.c | ||
| 17 | tests/threads_SRC += tests/threads/alarm-priority.c | ||
| 18 | tests/threads_SRC += tests/threads/alarm-zero.c | ||
| 19 | tests/threads_SRC += tests/threads/alarm-negative.c | ||
| 20 | tests/threads_SRC += tests/threads/priority-change.c | ||
| 21 | tests/threads_SRC += tests/threads/priority-donate-one.c | ||
| 22 | tests/threads_SRC += tests/threads/priority-donate-multiple.c | ||
| 23 | tests/threads_SRC += tests/threads/priority-donate-multiple2.c | ||
| 24 | tests/threads_SRC += tests/threads/priority-donate-nest.c | ||
| 25 | tests/threads_SRC += tests/threads/priority-donate-sema.c | ||
| 26 | tests/threads_SRC += tests/threads/priority-donate-lower.c | ||
| 27 | tests/threads_SRC += tests/threads/priority-fifo.c | ||
| 28 | tests/threads_SRC += tests/threads/priority-preempt.c | ||
| 29 | tests/threads_SRC += tests/threads/priority-sema.c | ||
| 30 | tests/threads_SRC += tests/threads/priority-condvar.c | ||
| 31 | tests/threads_SRC += tests/threads/priority-donate-chain.c | ||
| 32 | |||
| 33 | # Not used in SS 2012 | ||
| 34 | MLFQS_TESTS = mlfqs-load-1 mlfqs-load-60 mlfqs-load-avg mlfqs-recent-1 \ | ||
| 35 | mlfqs-fair-2 mlfqs-fair-20 mlfqs-nice-2 mlfqs-nice-10 mlfqs-block) | ||
| 36 | |||
| 37 | tests/threads_SRC += tests/threads/mlfqs-load-1.c | ||
| 38 | tests/threads_SRC += tests/threads/mlfqs-load-60.c | ||
| 39 | tests/threads_SRC += tests/threads/mlfqs-load-avg.c | ||
| 40 | tests/threads_SRC += tests/threads/mlfqs-recent-1.c | ||
| 41 | tests/threads_SRC += tests/threads/mlfqs-fair.c | ||
| 42 | tests/threads_SRC += tests/threads/mlfqs-block.c | ||
| 43 | |||
| 44 | MLFQS_OUTPUTS = \ | ||
| 45 | tests/threads/mlfqs-load-1.output \ | ||
| 46 | tests/threads/mlfqs-load-60.output \ | ||
| 47 | tests/threads/mlfqs-load-avg.output \ | ||
| 48 | tests/threads/mlfqs-recent-1.output \ | ||
| 49 | tests/threads/mlfqs-fair-2.output \ | ||
| 50 | tests/threads/mlfqs-fair-20.output \ | ||
| 51 | tests/threads/mlfqs-nice-2.output \ | ||
| 52 | tests/threads/mlfqs-nice-10.output \ | ||
| 53 | tests/threads/mlfqs-block.output | ||
| 54 | |||
| 55 | $(MLFQS_OUTPUTS): KERNELFLAGS += -mlfqs | ||
| 56 | $(MLFQS_OUTPUTS): TIMEOUT = 480 | ||
diff --git a/pintos-progos/tests/threads/Rubric.alarm b/pintos-progos/tests/threads/Rubric.alarm new file mode 100644 index 0000000..61abe85 --- /dev/null +++ b/pintos-progos/tests/threads/Rubric.alarm | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | Functionality and robustness of alarm clock: | ||
| 2 | 4 alarm-single | ||
| 3 | 4 alarm-multiple | ||
| 4 | 4 alarm-simultaneous | ||
| 5 | 4 alarm-priority | ||
| 6 | |||
| 7 | 1 alarm-zero | ||
| 8 | 1 alarm-negative | ||
diff --git a/pintos-progos/tests/threads/Rubric.mlfqs b/pintos-progos/tests/threads/Rubric.mlfqs new file mode 100644 index 0000000..f260091 --- /dev/null +++ b/pintos-progos/tests/threads/Rubric.mlfqs | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | Functionality of advanced scheduler: | ||
| 2 | 5 mlfqs-load-1 | ||
| 3 | 5 mlfqs-load-60 | ||
| 4 | 3 mlfqs-load-avg | ||
| 5 | |||
| 6 | 5 mlfqs-recent-1 | ||
| 7 | |||
| 8 | 5 mlfqs-fair-2 | ||
| 9 | 3 mlfqs-fair-20 | ||
| 10 | |||
| 11 | 4 mlfqs-nice-2 | ||
| 12 | 2 mlfqs-nice-10 | ||
| 13 | |||
| 14 | 5 mlfqs-block | ||
diff --git a/pintos-progos/tests/threads/Rubric.priority b/pintos-progos/tests/threads/Rubric.priority new file mode 100644 index 0000000..652bc99 --- /dev/null +++ b/pintos-progos/tests/threads/Rubric.priority | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | Functionality of priority scheduler: | ||
| 2 | 3 priority-change | ||
| 3 | 3 priority-preempt | ||
| 4 | |||
| 5 | 3 priority-fifo | ||
| 6 | 3 priority-sema | ||
| 7 | 3 priority-condvar | ||
| 8 | |||
| 9 | 3 priority-donate-one | ||
| 10 | 3 priority-donate-multiple | ||
| 11 | 3 priority-donate-multiple2 | ||
| 12 | 3 priority-donate-nest | ||
| 13 | 5 priority-donate-chain | ||
| 14 | 3 priority-donate-sema | ||
| 15 | 3 priority-donate-lower | ||
diff --git a/pintos-progos/tests/threads/alarm-multiple.ck b/pintos-progos/tests/threads/alarm-multiple.ck new file mode 100644 index 0000000..fd83bcd --- /dev/null +++ b/pintos-progos/tests/threads/alarm-multiple.ck | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use tests::tests; | ||
| 3 | use tests::threads::alarm; | ||
| 4 | check_alarm (7); | ||
diff --git a/pintos-progos/tests/threads/alarm-negative.c b/pintos-progos/tests/threads/alarm-negative.c new file mode 100644 index 0000000..aec52cf --- /dev/null +++ b/pintos-progos/tests/threads/alarm-negative.c | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | /* Tests timer_sleep(-100). Only requirement is that it not crash. */ | ||
| 2 | |||
| 3 | #include <stdio.h> | ||
| 4 | #include "tests/threads/tests.h" | ||
| 5 | #include "threads/malloc.h" | ||
| 6 | #include "threads/synch.h" | ||
| 7 | #include "threads/thread.h" | ||
| 8 | #include "devices/timer.h" | ||
| 9 | |||
| 10 | void | ||
| 11 | test_alarm_negative (void) | ||
| 12 | { | ||
| 13 | timer_sleep (-100); | ||
| 14 | pass (); | ||
| 15 | } | ||
diff --git a/pintos-progos/tests/threads/alarm-negative.ck b/pintos-progos/tests/threads/alarm-negative.ck new file mode 100644 index 0000000..0d2bab0 --- /dev/null +++ b/pintos-progos/tests/threads/alarm-negative.ck | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (alarm-negative) begin | ||
| 7 | (alarm-negative) PASS | ||
| 8 | (alarm-negative) end | ||
| 9 | EOF | ||
| 10 | pass; | ||
diff --git a/pintos-progos/tests/threads/alarm-priority.c b/pintos-progos/tests/threads/alarm-priority.c new file mode 100644 index 0000000..2288ff6 --- /dev/null +++ b/pintos-progos/tests/threads/alarm-priority.c | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | /* Checks that when the alarm clock wakes up threads, the | ||
| 2 | higher-priority threads run first. */ | ||
| 3 | |||
| 4 | #include <stdio.h> | ||
| 5 | #include "tests/threads/tests.h" | ||
| 6 | #include "threads/init.h" | ||
| 7 | #include "threads/malloc.h" | ||
| 8 | #include "threads/synch.h" | ||
| 9 | #include "threads/thread.h" | ||
| 10 | #include "devices/timer.h" | ||
| 11 | |||
| 12 | static thread_func alarm_priority_thread; | ||
| 13 | static int64_t wake_time; | ||
| 14 | static struct semaphore wait_sema; | ||
| 15 | |||
| 16 | void | ||
| 17 | test_alarm_priority (void) | ||
| 18 | { | ||
| 19 | int i; | ||
| 20 | |||
| 21 | /* This test does not work with the MLFQS. */ | ||
| 22 | ASSERT (!thread_mlfqs); | ||
| 23 | |||
| 24 | wake_time = timer_ticks () + 5 * TIMER_FREQ; | ||
| 25 | sema_init (&wait_sema, 0); | ||
| 26 | |||
| 27 | for (i = 0; i < 10; i++) | ||
| 28 | { | ||
| 29 | int priority = PRI_DEFAULT - (i + 5) % 10 - 1; | ||
| 30 | char name[16]; | ||
| 31 | snprintf (name, sizeof name, "priority %d", priority); | ||
| 32 | thread_create (name, priority, alarm_priority_thread, NULL); | ||
| 33 | } | ||
| 34 | |||
| 35 | thread_set_priority (PRI_MIN); | ||
| 36 | |||
| 37 | for (i = 0; i < 10; i++) | ||
| 38 | sema_down (&wait_sema); | ||
| 39 | } | ||
| 40 | |||
| 41 | static void | ||
| 42 | alarm_priority_thread (void *aux UNUSED) | ||
| 43 | { | ||
| 44 | /* Busy-wait until the current time changes. */ | ||
| 45 | int64_t start_time = timer_ticks (); | ||
| 46 | while (timer_elapsed (start_time) == 0) | ||
| 47 | continue; | ||
| 48 | |||
| 49 | /* Now we know we're at the very beginning of a timer tick, so | ||
| 50 | we can call timer_sleep() without worrying about races | ||
| 51 | between checking the time and a timer interrupt. */ | ||
| 52 | timer_sleep (wake_time - timer_ticks ()); | ||
| 53 | |||
| 54 | /* Print a message on wake-up. */ | ||
| 55 | msg ("Thread %s woke up.", thread_name ()); | ||
| 56 | |||
| 57 | sema_up (&wait_sema); | ||
| 58 | } | ||
diff --git a/pintos-progos/tests/threads/alarm-priority.ck b/pintos-progos/tests/threads/alarm-priority.ck new file mode 100644 index 0000000..b57c78b --- /dev/null +++ b/pintos-progos/tests/threads/alarm-priority.ck | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (alarm-priority) begin | ||
| 7 | (alarm-priority) Thread priority 30 woke up. | ||
| 8 | (alarm-priority) Thread priority 29 woke up. | ||
| 9 | (alarm-priority) Thread priority 28 woke up. | ||
| 10 | (alarm-priority) Thread priority 27 woke up. | ||
| 11 | (alarm-priority) Thread priority 26 woke up. | ||
| 12 | (alarm-priority) Thread priority 25 woke up. | ||
| 13 | (alarm-priority) Thread priority 24 woke up. | ||
| 14 | (alarm-priority) Thread priority 23 woke up. | ||
| 15 | (alarm-priority) Thread priority 22 woke up. | ||
| 16 | (alarm-priority) Thread priority 21 woke up. | ||
| 17 | (alarm-priority) end | ||
| 18 | EOF | ||
| 19 | pass; | ||
diff --git a/pintos-progos/tests/threads/alarm-simultaneous.c b/pintos-progos/tests/threads/alarm-simultaneous.c new file mode 100644 index 0000000..844eea4 --- /dev/null +++ b/pintos-progos/tests/threads/alarm-simultaneous.c | |||
| @@ -0,0 +1,94 @@ | |||
| 1 | /* Creates N threads, each of which sleeps a different, fixed | ||
| 2 | duration, M times. Records the wake-up order and verifies | ||
| 3 | that it is valid. */ | ||
| 4 | |||
| 5 | #include <stdio.h> | ||
| 6 | #include "tests/threads/tests.h" | ||
| 7 | #include "threads/init.h" | ||
| 8 | #include "threads/malloc.h" | ||
| 9 | #include "threads/synch.h" | ||
| 10 | #include "threads/thread.h" | ||
| 11 | #include "devices/timer.h" | ||
| 12 | |||
| 13 | static void test_sleep (int thread_cnt, int iterations); | ||
| 14 | |||
| 15 | void | ||
| 16 | test_alarm_simultaneous (void) | ||
| 17 | { | ||
| 18 | test_sleep (3, 5); | ||
| 19 | } | ||
| 20 | |||
| 21 | /* Information about the test. */ | ||
| 22 | struct sleep_test | ||
| 23 | { | ||
| 24 | int64_t start; /* Current time at start of test. */ | ||
| 25 | int iterations; /* Number of iterations per thread. */ | ||
| 26 | int *output_pos; /* Current position in output buffer. */ | ||
| 27 | }; | ||
| 28 | |||
| 29 | static void sleeper (void *); | ||
| 30 | |||
| 31 | /* Runs THREAD_CNT threads thread sleep ITERATIONS times each. */ | ||
| 32 | static void | ||
| 33 | test_sleep (int thread_cnt, int iterations) | ||
| 34 | { | ||
| 35 | struct sleep_test test; | ||
| 36 | int *output; | ||
| 37 | int i; | ||
| 38 | |||
| 39 | /* This test does not work with the MLFQS. */ | ||
| 40 | ASSERT (!thread_mlfqs); | ||
| 41 | |||
| 42 | msg ("Creating %d threads to sleep %d times each.", thread_cnt, iterations); | ||
| 43 | msg ("Each thread sleeps 10 ticks each time."); | ||
| 44 | msg ("Within an iteration, all threads should wake up on the same tick."); | ||
| 45 | |||
| 46 | /* Allocate memory. */ | ||
| 47 | output = malloc (sizeof *output * iterations * thread_cnt * 2); | ||
| 48 | if (output == NULL) | ||
| 49 | PANIC ("couldn't allocate memory for test"); | ||
| 50 | |||
| 51 | /* Initialize test. */ | ||
| 52 | test.start = timer_ticks () + 100; | ||
| 53 | test.iterations = iterations; | ||
| 54 | test.output_pos = output; | ||
| 55 | |||
| 56 | /* Start threads. */ | ||
| 57 | ASSERT (output != NULL); | ||
| 58 | for (i = 0; i < thread_cnt; i++) | ||
| 59 | { | ||
| 60 | char name[16]; | ||
| 61 | snprintf (name, sizeof name, "thread %d", i); | ||
| 62 | thread_create (name, PRI_DEFAULT, sleeper, &test); | ||
| 63 | } | ||
| 64 | |||
| 65 | /* Wait long enough for all the threads to finish. */ | ||
| 66 | timer_sleep (100 + iterations * 10 + 100); | ||
| 67 | |||
| 68 | /* Print completion order. */ | ||
| 69 | msg ("iteration 0, thread 0: woke up after %d ticks", output[0]); | ||
| 70 | for (i = 1; i < test.output_pos - output; i++) | ||
| 71 | msg ("iteration %d, thread %d: woke up %d ticks later", | ||
| 72 | i / thread_cnt, i % thread_cnt, output[i] - output[i - 1]); | ||
| 73 | |||
| 74 | free (output); | ||
| 75 | } | ||
| 76 | |||
| 77 | /* Sleeper thread. */ | ||
| 78 | static void | ||
| 79 | sleeper (void *test_) | ||
| 80 | { | ||
| 81 | struct sleep_test *test = test_; | ||
| 82 | int i; | ||
| 83 | |||
| 84 | /* Make sure we're at the beginning of a timer tick. */ | ||
| 85 | timer_sleep (1); | ||
| 86 | |||
| 87 | for (i = 1; i <= test->iterations; i++) | ||
| 88 | { | ||
| 89 | int64_t sleep_until = test->start + i * 10; | ||
| 90 | timer_sleep (sleep_until - timer_ticks ()); | ||
| 91 | *test->output_pos++ = timer_ticks () - test->start; | ||
| 92 | thread_yield (); | ||
| 93 | } | ||
| 94 | } | ||
diff --git a/pintos-progos/tests/threads/alarm-simultaneous.ck b/pintos-progos/tests/threads/alarm-simultaneous.ck new file mode 100644 index 0000000..406b8b0 --- /dev/null +++ b/pintos-progos/tests/threads/alarm-simultaneous.ck | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (alarm-simultaneous) begin | ||
| 7 | (alarm-simultaneous) Creating 3 threads to sleep 5 times each. | ||
| 8 | (alarm-simultaneous) Each thread sleeps 10 ticks each time. | ||
| 9 | (alarm-simultaneous) Within an iteration, all threads should wake up on the same tick. | ||
| 10 | (alarm-simultaneous) iteration 0, thread 0: woke up after 10 ticks | ||
| 11 | (alarm-simultaneous) iteration 0, thread 1: woke up 0 ticks later | ||
| 12 | (alarm-simultaneous) iteration 0, thread 2: woke up 0 ticks later | ||
| 13 | (alarm-simultaneous) iteration 1, thread 0: woke up 10 ticks later | ||
| 14 | (alarm-simultaneous) iteration 1, thread 1: woke up 0 ticks later | ||
| 15 | (alarm-simultaneous) iteration 1, thread 2: woke up 0 ticks later | ||
| 16 | (alarm-simultaneous) iteration 2, thread 0: woke up 10 ticks later | ||
| 17 | (alarm-simultaneous) iteration 2, thread 1: woke up 0 ticks later | ||
| 18 | (alarm-simultaneous) iteration 2, thread 2: woke up 0 ticks later | ||
| 19 | (alarm-simultaneous) iteration 3, thread 0: woke up 10 ticks later | ||
| 20 | (alarm-simultaneous) iteration 3, thread 1: woke up 0 ticks later | ||
| 21 | (alarm-simultaneous) iteration 3, thread 2: woke up 0 ticks later | ||
| 22 | (alarm-simultaneous) iteration 4, thread 0: woke up 10 ticks later | ||
| 23 | (alarm-simultaneous) iteration 4, thread 1: woke up 0 ticks later | ||
| 24 | (alarm-simultaneous) iteration 4, thread 2: woke up 0 ticks later | ||
| 25 | (alarm-simultaneous) end | ||
| 26 | EOF | ||
| 27 | pass; | ||
diff --git a/pintos-progos/tests/threads/alarm-single.ck b/pintos-progos/tests/threads/alarm-single.ck new file mode 100644 index 0000000..31215df --- /dev/null +++ b/pintos-progos/tests/threads/alarm-single.ck | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use tests::tests; | ||
| 3 | use tests::threads::alarm; | ||
| 4 | check_alarm (1); | ||
diff --git a/pintos-progos/tests/threads/alarm-wait.c b/pintos-progos/tests/threads/alarm-wait.c new file mode 100644 index 0000000..37d3afc --- /dev/null +++ b/pintos-progos/tests/threads/alarm-wait.c | |||
| @@ -0,0 +1,152 @@ | |||
| 1 | /* Creates N threads, each of which sleeps a different, fixed | ||
| 2 | duration, M times. Records the wake-up order and verifies | ||
| 3 | that it is valid. */ | ||
| 4 | |||
| 5 | #include <stdio.h> | ||
| 6 | #include "tests/threads/tests.h" | ||
| 7 | #include "threads/init.h" | ||
| 8 | #include "threads/malloc.h" | ||
| 9 | #include "threads/synch.h" | ||
| 10 | #include "threads/thread.h" | ||
| 11 | #include "devices/timer.h" | ||
| 12 | |||
| 13 | static void test_sleep (int thread_cnt, int iterations); | ||
| 14 | |||
| 15 | void | ||
| 16 | test_alarm_single (void) | ||
| 17 | { | ||
| 18 | test_sleep (5, 1); | ||
| 19 | } | ||
| 20 | |||
| 21 | void | ||
| 22 | test_alarm_multiple (void) | ||
| 23 | { | ||
| 24 | test_sleep (5, 7); | ||
| 25 | } | ||
| 26 | |||
| 27 | /* Information about the test. */ | ||
| 28 | struct sleep_test | ||
| 29 | { | ||
| 30 | int64_t start; /* Current time at start of test. */ | ||
| 31 | int iterations; /* Number of iterations per thread. */ | ||
| 32 | |||
| 33 | /* Output. */ | ||
| 34 | struct lock output_lock; /* Lock protecting output buffer. */ | ||
| 35 | int *output_pos; /* Current position in output buffer. */ | ||
| 36 | }; | ||
| 37 | |||
| 38 | /* Information about an individual thread in the test. */ | ||
| 39 | struct sleep_thread | ||
| 40 | { | ||
| 41 | struct sleep_test *test; /* Info shared between all threads. */ | ||
| 42 | int id; /* Sleeper ID. */ | ||
| 43 | int duration; /* Number of ticks to sleep. */ | ||
| 44 | int iterations; /* Iterations counted so far. */ | ||
| 45 | }; | ||
| 46 | |||
| 47 | static void sleeper (void *); | ||
| 48 | |||
| 49 | /* Runs THREAD_CNT threads thread sleep ITERATIONS times each. */ | ||
| 50 | static void | ||
| 51 | test_sleep (int thread_cnt, int iterations) | ||
| 52 | { | ||
| 53 | struct sleep_test test; | ||
| 54 | struct sleep_thread *threads; | ||
| 55 | int *output, *op; | ||
| 56 | int product; | ||
| 57 | int i; | ||
| 58 | |||
| 59 | /* This test does not work with the MLFQS. */ | ||
| 60 | ASSERT (!thread_mlfqs); | ||
| 61 | |||
| 62 | msg ("Creating %d threads to sleep %d times each.", thread_cnt, iterations); | ||
| 63 | msg ("Thread 0 sleeps 10 ticks each time,"); | ||
| 64 | msg ("thread 1 sleeps 20 ticks each time, and so on."); | ||
| 65 | msg ("If successful, product of iteration count and"); | ||
| 66 | msg ("sleep duration will appear in nondescending order."); | ||
| 67 | |||
| 68 | /* Allocate memory. */ | ||
| 69 | threads = malloc (sizeof *threads * thread_cnt); | ||
| 70 | output = malloc (sizeof *output * iterations * thread_cnt * 2); | ||
| 71 | if (threads == NULL || output == NULL) | ||
| 72 | PANIC ("couldn't allocate memory for test"); | ||
| 73 | |||
| 74 | /* Initialize test. */ | ||
| 75 | test.start = timer_ticks () + 100; | ||
| 76 | test.iterations = iterations; | ||
| 77 | lock_init (&test.output_lock); | ||
| 78 | test.output_pos = output; | ||
| 79 | |||
| 80 | /* Start threads. */ | ||
| 81 | ASSERT (output != NULL); | ||
| 82 | for (i = 0; i < thread_cnt; i++) | ||
| 83 | { | ||
| 84 | struct sleep_thread *t = threads + i; | ||
| 85 | char name[16]; | ||
| 86 | |||
| 87 | t->test = &test; | ||
| 88 | t->id = i; | ||
| 89 | t->duration = (i + 1) * 10; | ||
| 90 | t->iterations = 0; | ||
| 91 | |||
| 92 | snprintf (name, sizeof name, "thread %d", i); | ||
| 93 | thread_create (name, PRI_DEFAULT, sleeper, t); | ||
| 94 | } | ||
| 95 | |||
| 96 | /* Wait long enough for all the threads to finish. */ | ||
| 97 | timer_sleep (100 + thread_cnt * iterations * 10 + 100); | ||
| 98 | |||
| 99 | /* Acquire the output lock in case some rogue thread is still | ||
| 100 | running. */ | ||
| 101 | lock_acquire (&test.output_lock); | ||
| 102 | |||
| 103 | /* Print completion order. */ | ||
| 104 | product = 0; | ||
| 105 | for (op = output; op < test.output_pos; op++) | ||
| 106 | { | ||
| 107 | struct sleep_thread *t; | ||
| 108 | int new_prod; | ||
| 109 | |||
| 110 | ASSERT (*op >= 0 && *op < thread_cnt); | ||
| 111 | t = threads + *op; | ||
| 112 | |||
| 113 | new_prod = ++t->iterations * t->duration; | ||
| 114 | |||
| 115 | msg ("thread %d: duration=%d, iteration=%d, product=%d", | ||
| 116 | t->id, t->duration, t->iterations, new_prod); | ||
| 117 | |||
| 118 | if (new_prod >= product) | ||
| 119 | product = new_prod; | ||
| 120 | else | ||
| 121 | fail ("thread %d woke up out of order (%d > %d)!", | ||
| 122 | t->id, product, new_prod); | ||
| 123 | } | ||
| 124 | |||
| 125 | /* Verify that we had the proper number of wakeups. */ | ||
| 126 | for (i = 0; i < thread_cnt; i++) | ||
| 127 | if (threads[i].iterations != iterations) | ||
| 128 | fail ("thread %d woke up %d times instead of %d", | ||
| 129 | i, threads[i].iterations, iterations); | ||
| 130 | |||
| 131 | lock_release (&test.output_lock); | ||
| 132 | free (output); | ||
| 133 | free (threads); | ||
| 134 | } | ||
| 135 | |||
| 136 | /* Sleeper thread. */ | ||
| 137 | static void | ||
| 138 | sleeper (void *t_) | ||
| 139 | { | ||
| 140 | struct sleep_thread *t = t_; | ||
| 141 | struct sleep_test *test = t->test; | ||
| 142 | int i; | ||
| 143 | |||
| 144 | for (i = 1; i <= test->iterations; i++) | ||
| 145 | { | ||
| 146 | int64_t sleep_until = test->start + i * t->duration; | ||
| 147 | timer_sleep (sleep_until - timer_ticks ()); | ||
| 148 | lock_acquire (&test->output_lock); | ||
| 149 | *test->output_pos++ = t->id; | ||
| 150 | lock_release (&test->output_lock); | ||
| 151 | } | ||
| 152 | } | ||
diff --git a/pintos-progos/tests/threads/alarm-zero.c b/pintos-progos/tests/threads/alarm-zero.c new file mode 100644 index 0000000..c8a3ee2 --- /dev/null +++ b/pintos-progos/tests/threads/alarm-zero.c | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | /* Tests timer_sleep(0), which should return immediately. */ | ||
| 2 | |||
| 3 | #include <stdio.h> | ||
| 4 | #include "tests/threads/tests.h" | ||
| 5 | #include "threads/malloc.h" | ||
| 6 | #include "threads/synch.h" | ||
| 7 | #include "threads/thread.h" | ||
| 8 | #include "devices/timer.h" | ||
| 9 | |||
| 10 | void | ||
| 11 | test_alarm_zero (void) | ||
| 12 | { | ||
| 13 | timer_sleep (0); | ||
| 14 | pass (); | ||
| 15 | } | ||
diff --git a/pintos-progos/tests/threads/alarm-zero.ck b/pintos-progos/tests/threads/alarm-zero.ck new file mode 100644 index 0000000..a6b1a3c --- /dev/null +++ b/pintos-progos/tests/threads/alarm-zero.ck | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (alarm-zero) begin | ||
| 7 | (alarm-zero) PASS | ||
| 8 | (alarm-zero) end | ||
| 9 | EOF | ||
| 10 | pass; | ||
diff --git a/pintos-progos/tests/threads/alarm.pm b/pintos-progos/tests/threads/alarm.pm new file mode 100644 index 0000000..84b3b7f --- /dev/null +++ b/pintos-progos/tests/threads/alarm.pm | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | sub check_alarm { | ||
| 2 | my ($iterations) = @_; | ||
| 3 | our ($test); | ||
| 4 | |||
| 5 | @output = read_text_file ("$test.output"); | ||
| 6 | common_checks ("run", @output); | ||
| 7 | |||
| 8 | my (@products); | ||
| 9 | for (my ($i) = 0; $i < $iterations; $i++) { | ||
| 10 | for (my ($t) = 0; $t < 5; $t++) { | ||
| 11 | push (@products, ($i + 1) * ($t + 1) * 10); | ||
| 12 | } | ||
| 13 | } | ||
| 14 | @products = sort {$a <=> $b} @products; | ||
| 15 | |||
| 16 | local ($_); | ||
| 17 | foreach (@output) { | ||
| 18 | fail $_ if /out of order/i; | ||
| 19 | |||
| 20 | my ($p) = /product=(\d+)$/; | ||
| 21 | next if !defined $p; | ||
| 22 | |||
| 23 | my ($q) = shift (@products); | ||
| 24 | fail "Too many wakeups.\n" if !defined $q; | ||
| 25 | fail "Out of order wakeups ($p vs. $q).\n" if $p != $q; # FIXME | ||
| 26 | } | ||
| 27 | fail scalar (@products) . " fewer wakeups than expected.\n" | ||
| 28 | if @products != 0; | ||
| 29 | pass; | ||
| 30 | } | ||
| 31 | |||
| 32 | 1; | ||
diff --git a/pintos-progos/tests/threads/mlfqs-block.c b/pintos-progos/tests/threads/mlfqs-block.c new file mode 100644 index 0000000..6d4992d --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-block.c | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | /* Checks that recent_cpu and priorities are updated for blocked | ||
| 2 | threads. | ||
| 3 | |||
| 4 | The main thread sleeps for 25 seconds, spins for 5 seconds, | ||
| 5 | then releases a lock. The "block" thread spins for 20 seconds | ||
| 6 | then attempts to acquire the lock, which will block for 10 | ||
| 7 | seconds (until the main thread releases it). If recent_cpu | ||
| 8 | decays properly while the "block" thread sleeps, then the | ||
| 9 | block thread should be immediately scheduled when the main | ||
| 10 | thread releases the lock. */ | ||
| 11 | |||
| 12 | #include <stdio.h> | ||
| 13 | #include "tests/threads/tests.h" | ||
| 14 | #include "threads/init.h" | ||
| 15 | #include "threads/malloc.h" | ||
| 16 | #include "threads/synch.h" | ||
| 17 | #include "threads/thread.h" | ||
| 18 | #include "devices/timer.h" | ||
| 19 | |||
| 20 | static void block_thread (void *lock_); | ||
| 21 | |||
| 22 | void | ||
| 23 | test_mlfqs_block (void) | ||
| 24 | { | ||
| 25 | int64_t start_time; | ||
| 26 | struct lock lock; | ||
| 27 | |||
| 28 | ASSERT (thread_mlfqs); | ||
| 29 | |||
| 30 | msg ("Main thread acquiring lock."); | ||
| 31 | lock_init (&lock); | ||
| 32 | lock_acquire (&lock); | ||
| 33 | |||
| 34 | msg ("Main thread creating block thread, sleeping 25 seconds..."); | ||
| 35 | thread_create ("block", PRI_DEFAULT, block_thread, &lock); | ||
| 36 | timer_sleep (25 * TIMER_FREQ); | ||
| 37 | |||
| 38 | msg ("Main thread spinning for 5 seconds..."); | ||
| 39 | start_time = timer_ticks (); | ||
| 40 | while (timer_elapsed (start_time) < 5 * TIMER_FREQ) | ||
| 41 | continue; | ||
| 42 | |||
| 43 | msg ("Main thread releasing lock."); | ||
| 44 | lock_release (&lock); | ||
| 45 | |||
| 46 | msg ("Block thread should have already acquired lock."); | ||
| 47 | } | ||
| 48 | |||
| 49 | static void | ||
| 50 | block_thread (void *lock_) | ||
| 51 | { | ||
| 52 | struct lock *lock = lock_; | ||
| 53 | int64_t start_time; | ||
| 54 | |||
| 55 | msg ("Block thread spinning for 20 seconds..."); | ||
| 56 | start_time = timer_ticks (); | ||
| 57 | while (timer_elapsed (start_time) < 20 * TIMER_FREQ) | ||
| 58 | continue; | ||
| 59 | |||
| 60 | msg ("Block thread acquiring lock..."); | ||
| 61 | lock_acquire (lock); | ||
| 62 | |||
| 63 | msg ("...got it."); | ||
| 64 | } | ||
diff --git a/pintos-progos/tests/threads/mlfqs-block.ck b/pintos-progos/tests/threads/mlfqs-block.ck new file mode 100644 index 0000000..8833a3a --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-block.ck | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (mlfqs-block) begin | ||
| 7 | (mlfqs-block) Main thread acquiring lock. | ||
| 8 | (mlfqs-block) Main thread creating block thread, sleeping 25 seconds... | ||
| 9 | (mlfqs-block) Block thread spinning for 20 seconds... | ||
| 10 | (mlfqs-block) Block thread acquiring lock... | ||
| 11 | (mlfqs-block) Main thread spinning for 5 seconds... | ||
| 12 | (mlfqs-block) Main thread releasing lock. | ||
| 13 | (mlfqs-block) ...got it. | ||
| 14 | (mlfqs-block) Block thread should have already acquired lock. | ||
| 15 | (mlfqs-block) end | ||
| 16 | EOF | ||
| 17 | pass; | ||
diff --git a/pintos-progos/tests/threads/mlfqs-fair-2.ck b/pintos-progos/tests/threads/mlfqs-fair-2.ck new file mode 100644 index 0000000..5b19ff1 --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-fair-2.ck | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::threads::mlfqs; | ||
| 6 | |||
| 7 | check_mlfqs_fair ([0, 0], 50); | ||
diff --git a/pintos-progos/tests/threads/mlfqs-fair-20.ck b/pintos-progos/tests/threads/mlfqs-fair-20.ck new file mode 100644 index 0000000..bb4d051 --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-fair-20.ck | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::threads::mlfqs; | ||
| 6 | |||
| 7 | check_mlfqs_fair ([(0) x 20], 20); | ||
diff --git a/pintos-progos/tests/threads/mlfqs-fair.c b/pintos-progos/tests/threads/mlfqs-fair.c new file mode 100644 index 0000000..3b1bea5 --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-fair.c | |||
| @@ -0,0 +1,124 @@ | |||
| 1 | /* Measures the correctness of the "nice" implementation. | ||
| 2 | |||
| 3 | The "fair" tests run either 2 or 20 threads all niced to 0. | ||
| 4 | The threads should all receive approximately the same number | ||
| 5 | of ticks. Each test runs for 30 seconds, so the ticks should | ||
| 6 | also sum to approximately 30 * 100 == 3000 ticks. | ||
| 7 | |||
| 8 | The mlfqs-nice-2 test runs 2 threads, one with nice 0, the | ||
| 9 | other with nice 5, which should receive 1,904 and 1,096 ticks, | ||
| 10 | respectively, over 30 seconds. | ||
| 11 | |||
| 12 | The mlfqs-nice-10 test runs 10 threads with nice 0 through 9. | ||
| 13 | They should receive 672, 588, 492, 408, 316, 232, 152, 92, 40, | ||
| 14 | and 8 ticks, respectively, over 30 seconds. | ||
| 15 | |||
| 16 | (The above are computed via simulation in mlfqs.pm.) */ | ||
| 17 | |||
| 18 | #include <stdio.h> | ||
| 19 | #include <inttypes.h> | ||
| 20 | #include "tests/threads/tests.h" | ||
| 21 | #include "threads/init.h" | ||
| 22 | #include "threads/malloc.h" | ||
| 23 | #include "threads/palloc.h" | ||
| 24 | #include "threads/synch.h" | ||
| 25 | #include "threads/thread.h" | ||
| 26 | #include "devices/timer.h" | ||
| 27 | |||
| 28 | static void test_mlfqs_fair (int thread_cnt, int nice_min, int nice_step); | ||
| 29 | |||
| 30 | void | ||
| 31 | test_mlfqs_fair_2 (void) | ||
| 32 | { | ||
| 33 | test_mlfqs_fair (2, 0, 0); | ||
| 34 | } | ||
| 35 | |||
| 36 | void | ||
| 37 | test_mlfqs_fair_20 (void) | ||
| 38 | { | ||
| 39 | test_mlfqs_fair (20, 0, 0); | ||
| 40 | } | ||
| 41 | |||
| 42 | void | ||
| 43 | test_mlfqs_nice_2 (void) | ||
| 44 | { | ||
| 45 | test_mlfqs_fair (2, 0, 5); | ||
| 46 | } | ||
| 47 | |||
| 48 | void | ||
| 49 | test_mlfqs_nice_10 (void) | ||
| 50 | { | ||
| 51 | test_mlfqs_fair (10, 0, 1); | ||
| 52 | } | ||
| 53 | |||
| 54 | #define MAX_THREAD_CNT 20 | ||
| 55 | |||
| 56 | struct thread_info | ||
| 57 | { | ||
| 58 | int64_t start_time; | ||
| 59 | int tick_count; | ||
| 60 | int nice; | ||
| 61 | }; | ||
| 62 | |||
| 63 | static void load_thread (void *aux); | ||
| 64 | |||
| 65 | static void | ||
| 66 | test_mlfqs_fair (int thread_cnt, int nice_min, int nice_step) | ||
| 67 | { | ||
| 68 | struct thread_info info[MAX_THREAD_CNT]; | ||
| 69 | int64_t start_time; | ||
| 70 | int nice; | ||
| 71 | int i; | ||
| 72 | |||
| 73 | ASSERT (thread_mlfqs); | ||
| 74 | ASSERT (thread_cnt <= MAX_THREAD_CNT); | ||
| 75 | ASSERT (nice_min >= -10); | ||
| 76 | ASSERT (nice_step >= 0); | ||
| 77 | ASSERT (nice_min + nice_step * (thread_cnt - 1) <= 20); | ||
| 78 | |||
| 79 | thread_set_nice (-20); | ||
| 80 | |||
| 81 | start_time = timer_ticks (); | ||
| 82 | msg ("Starting %d threads...", thread_cnt); | ||
| 83 | nice = nice_min; | ||
| 84 | for (i = 0; i < thread_cnt; i++) | ||
| 85 | { | ||
| 86 | struct thread_info *ti = &info[i]; | ||
| 87 | char name[16]; | ||
| 88 | |||
| 89 | ti->start_time = start_time; | ||
| 90 | ti->tick_count = 0; | ||
| 91 | ti->nice = nice; | ||
| 92 | |||
| 93 | snprintf(name, sizeof name, "load %d", i); | ||
| 94 | thread_create (name, PRI_DEFAULT, load_thread, ti); | ||
| 95 | |||
| 96 | nice += nice_step; | ||
| 97 | } | ||
| 98 | msg ("Starting threads took %"PRId64" ticks.", timer_elapsed (start_time)); | ||
| 99 | |||
| 100 | msg ("Sleeping 40 seconds to let threads run, please wait..."); | ||
| 101 | timer_sleep (40 * TIMER_FREQ); | ||
| 102 | |||
| 103 | for (i = 0; i < thread_cnt; i++) | ||
| 104 | msg ("Thread %d received %d ticks.", i, info[i].tick_count); | ||
| 105 | } | ||
| 106 | |||
| 107 | static void | ||
| 108 | load_thread (void *ti_) | ||
| 109 | { | ||
| 110 | struct thread_info *ti = ti_; | ||
| 111 | int64_t sleep_time = 5 * TIMER_FREQ; | ||
| 112 | int64_t spin_time = sleep_time + 30 * TIMER_FREQ; | ||
| 113 | int64_t last_time = 0; | ||
| 114 | |||
| 115 | thread_set_nice (ti->nice); | ||
| 116 | timer_sleep (sleep_time - timer_elapsed (ti->start_time)); | ||
| 117 | while (timer_elapsed (ti->start_time) < spin_time) | ||
| 118 | { | ||
| 119 | int64_t cur_time = timer_ticks (); | ||
| 120 | if (cur_time != last_time) | ||
| 121 | ti->tick_count++; | ||
| 122 | last_time = cur_time; | ||
| 123 | } | ||
| 124 | } | ||
diff --git a/pintos-progos/tests/threads/mlfqs-load-1.c b/pintos-progos/tests/threads/mlfqs-load-1.c new file mode 100644 index 0000000..a39eea2 --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-load-1.c | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | /* Verifies that a single busy thread raises the load average to | ||
| 2 | 0.5 in 38 to 45 seconds. The expected time is 42 seconds, as | ||
| 3 | you can verify: | ||
| 4 | perl -e '$i++,$a=(59*$a+1)/60while$a<=.5;print "$i\n"' | ||
| 5 | |||
| 6 | Then, verifies that 10 seconds of inactivity drop the load | ||
| 7 | average back below 0.5 again. */ | ||
| 8 | |||
| 9 | #include <stdio.h> | ||
| 10 | #include "tests/threads/tests.h" | ||
| 11 | #include "threads/init.h" | ||
| 12 | #include "threads/malloc.h" | ||
| 13 | #include "threads/synch.h" | ||
| 14 | #include "threads/thread.h" | ||
| 15 | #include "devices/timer.h" | ||
| 16 | |||
| 17 | void | ||
| 18 | test_mlfqs_load_1 (void) | ||
| 19 | { | ||
| 20 | int64_t start_time; | ||
| 21 | int elapsed; | ||
| 22 | int load_avg; | ||
| 23 | |||
| 24 | ASSERT (thread_mlfqs); | ||
| 25 | |||
| 26 | msg ("spinning for up to 45 seconds, please wait..."); | ||
| 27 | |||
| 28 | start_time = timer_ticks (); | ||
| 29 | for (;;) | ||
| 30 | { | ||
| 31 | load_avg = thread_get_load_avg (); | ||
| 32 | ASSERT (load_avg >= 0); | ||
| 33 | elapsed = timer_elapsed (start_time) / TIMER_FREQ; | ||
| 34 | if (load_avg > 100) | ||
| 35 | fail ("load average is %d.%02d " | ||
| 36 | "but should be between 0 and 1 (after %d seconds)", | ||
| 37 | load_avg / 100, load_avg % 100, elapsed); | ||
| 38 | else if (load_avg > 50) | ||
| 39 | break; | ||
| 40 | else if (elapsed > 45) | ||
| 41 | fail ("load average stayed below 0.5 for more than 45 seconds"); | ||
| 42 | } | ||
| 43 | |||
| 44 | if (elapsed < 38) | ||
| 45 | fail ("load average took only %d seconds to rise above 0.5", elapsed); | ||
| 46 | msg ("load average rose to 0.5 after %d seconds", elapsed); | ||
| 47 | |||
| 48 | msg ("sleeping for another 10 seconds, please wait..."); | ||
| 49 | timer_sleep (TIMER_FREQ * 10); | ||
| 50 | |||
| 51 | load_avg = thread_get_load_avg (); | ||
| 52 | if (load_avg < 0) | ||
| 53 | fail ("load average fell below 0"); | ||
| 54 | if (load_avg > 50) | ||
| 55 | fail ("load average stayed above 0.5 for more than 10 seconds"); | ||
| 56 | msg ("load average fell back below 0.5 (to %d.%02d)", | ||
| 57 | load_avg / 100, load_avg % 100); | ||
| 58 | |||
| 59 | pass (); | ||
| 60 | } | ||
diff --git a/pintos-progos/tests/threads/mlfqs-load-1.ck b/pintos-progos/tests/threads/mlfqs-load-1.ck new file mode 100644 index 0000000..faf0ffa --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-load-1.ck | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | |||
| 6 | our ($test); | ||
| 7 | my (@output) = read_text_file ("$test.output"); | ||
| 8 | |||
| 9 | common_checks ("run", @output); | ||
| 10 | |||
| 11 | @output = get_core_output ("run", @output); | ||
| 12 | fail "missing PASS in output" | ||
| 13 | unless grep ($_ eq '(mlfqs-load-1) PASS', @output); | ||
| 14 | |||
| 15 | pass; | ||
diff --git a/pintos-progos/tests/threads/mlfqs-load-60.c b/pintos-progos/tests/threads/mlfqs-load-60.c new file mode 100644 index 0000000..b6a3eb6 --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-load-60.c | |||
| @@ -0,0 +1,155 @@ | |||
| 1 | /* Starts 60 threads that each sleep for 10 seconds, then spin in | ||
| 2 | a tight loop for 60 seconds, and sleep for another 60 seconds. | ||
| 3 | Every 2 seconds after the initial sleep, the main thread | ||
| 4 | prints the load average. | ||
| 5 | |||
| 6 | The expected output is this (some margin of error is allowed): | ||
| 7 | |||
| 8 | After 0 seconds, load average=1.00. | ||
| 9 | After 2 seconds, load average=2.95. | ||
| 10 | After 4 seconds, load average=4.84. | ||
| 11 | After 6 seconds, load average=6.66. | ||
| 12 | After 8 seconds, load average=8.42. | ||
| 13 | After 10 seconds, load average=10.13. | ||
| 14 | After 12 seconds, load average=11.78. | ||
| 15 | After 14 seconds, load average=13.37. | ||
| 16 | After 16 seconds, load average=14.91. | ||
| 17 | After 18 seconds, load average=16.40. | ||
| 18 | After 20 seconds, load average=17.84. | ||
| 19 | After 22 seconds, load average=19.24. | ||
| 20 | After 24 seconds, load average=20.58. | ||
| 21 | After 26 seconds, load average=21.89. | ||
| 22 | After 28 seconds, load average=23.15. | ||
| 23 | After 30 seconds, load average=24.37. | ||
| 24 | After 32 seconds, load average=25.54. | ||
| 25 | After 34 seconds, load average=26.68. | ||
| 26 | After 36 seconds, load average=27.78. | ||
| 27 | After 38 seconds, load average=28.85. | ||
| 28 | After 40 seconds, load average=29.88. | ||
| 29 | After 42 seconds, load average=30.87. | ||
| 30 | After 44 seconds, load average=31.84. | ||
| 31 | After 46 seconds, load average=32.77. | ||
| 32 | After 48 seconds, load average=33.67. | ||
| 33 | After 50 seconds, load average=34.54. | ||
| 34 | After 52 seconds, load average=35.38. | ||
| 35 | After 54 seconds, load average=36.19. | ||
| 36 | After 56 seconds, load average=36.98. | ||
| 37 | After 58 seconds, load average=37.74. | ||
| 38 | After 60 seconds, load average=37.48. | ||
| 39 | After 62 seconds, load average=36.24. | ||
| 40 | After 64 seconds, load average=35.04. | ||
| 41 | After 66 seconds, load average=33.88. | ||
| 42 | After 68 seconds, load average=32.76. | ||
| 43 | After 70 seconds, load average=31.68. | ||
| 44 | After 72 seconds, load average=30.63. | ||
| 45 | After 74 seconds, load average=29.62. | ||
| 46 | After 76 seconds, load average=28.64. | ||
| 47 | After 78 seconds, load average=27.69. | ||
| 48 | After 80 seconds, load average=26.78. | ||
| 49 | After 82 seconds, load average=25.89. | ||
| 50 | After 84 seconds, load average=25.04. | ||
| 51 | After 86 seconds, load average=24.21. | ||
| 52 | After 88 seconds, load average=23.41. | ||
| 53 | After 90 seconds, load average=22.64. | ||
| 54 | After 92 seconds, load average=21.89. | ||
| 55 | After 94 seconds, load average=21.16. | ||
| 56 | After 96 seconds, load average=20.46. | ||
| 57 | After 98 seconds, load average=19.79. | ||
| 58 | After 100 seconds, load average=19.13. | ||
| 59 | After 102 seconds, load average=18.50. | ||
| 60 | After 104 seconds, load average=17.89. | ||
| 61 | After 106 seconds, load average=17.30. | ||
| 62 | After 108 seconds, load average=16.73. | ||
| 63 | After 110 seconds, load average=16.17. | ||
| 64 | After 112 seconds, load average=15.64. | ||
| 65 | After 114 seconds, load average=15.12. | ||
| 66 | After 116 seconds, load average=14.62. | ||
| 67 | After 118 seconds, load average=14.14. | ||
| 68 | After 120 seconds, load average=13.67. | ||
| 69 | After 122 seconds, load average=13.22. | ||
| 70 | After 124 seconds, load average=12.78. | ||
| 71 | After 126 seconds, load average=12.36. | ||
| 72 | After 128 seconds, load average=11.95. | ||
| 73 | After 130 seconds, load average=11.56. | ||
| 74 | After 132 seconds, load average=11.17. | ||
| 75 | After 134 seconds, load average=10.80. | ||
| 76 | After 136 seconds, load average=10.45. | ||
| 77 | After 138 seconds, load average=10.10. | ||
| 78 | After 140 seconds, load average=9.77. | ||
| 79 | After 142 seconds, load average=9.45. | ||
| 80 | After 144 seconds, load average=9.13. | ||
| 81 | After 146 seconds, load average=8.83. | ||
| 82 | After 148 seconds, load average=8.54. | ||
| 83 | After 150 seconds, load average=8.26. | ||
| 84 | After 152 seconds, load average=7.98. | ||
| 85 | After 154 seconds, load average=7.72. | ||
| 86 | After 156 seconds, load average=7.47. | ||
| 87 | After 158 seconds, load average=7.22. | ||
| 88 | After 160 seconds, load average=6.98. | ||
| 89 | After 162 seconds, load average=6.75. | ||
| 90 | After 164 seconds, load average=6.53. | ||
| 91 | After 166 seconds, load average=6.31. | ||
| 92 | After 168 seconds, load average=6.10. | ||
| 93 | After 170 seconds, load average=5.90. | ||
| 94 | After 172 seconds, load average=5.70. | ||
| 95 | After 174 seconds, load average=5.52. | ||
| 96 | After 176 seconds, load average=5.33. | ||
| 97 | After 178 seconds, load average=5.16. | ||
| 98 | */ | ||
| 99 | |||
| 100 | #include <stdio.h> | ||
| 101 | #include "tests/threads/tests.h" | ||
| 102 | #include "threads/init.h" | ||
| 103 | #include "threads/malloc.h" | ||
| 104 | #include "threads/synch.h" | ||
| 105 | #include "threads/thread.h" | ||
| 106 | #include "devices/timer.h" | ||
| 107 | |||
| 108 | static int64_t start_time; | ||
| 109 | |||
| 110 | static void load_thread (void *aux); | ||
| 111 | |||
| 112 | #define THREAD_CNT 60 | ||
| 113 | |||
| 114 | void | ||
| 115 | test_mlfqs_load_60 (void) | ||
| 116 | { | ||
| 117 | int i; | ||
| 118 | |||
| 119 | ASSERT (thread_mlfqs); | ||
| 120 | |||
| 121 | start_time = timer_ticks (); | ||
| 122 | msg ("Starting %d niced load threads...", THREAD_CNT); | ||
| 123 | for (i = 0; i < THREAD_CNT; i++) | ||
| 124 | { | ||
| 125 | char name[16]; | ||
| 126 | snprintf(name, sizeof name, "load %d", i); | ||
| 127 | thread_create (name, PRI_DEFAULT, load_thread, NULL); | ||
| 128 | } | ||
| 129 | msg ("Starting threads took %d seconds.", | ||
| 130 | timer_elapsed (start_time) / TIMER_FREQ); | ||
| 131 | |||
| 132 | for (i = 0; i < 90; i++) | ||
| 133 | { | ||
| 134 | int64_t sleep_until = start_time + TIMER_FREQ * (2 * i + 10); | ||
| 135 | int load_avg; | ||
| 136 | timer_sleep (sleep_until - timer_ticks ()); | ||
| 137 | load_avg = thread_get_load_avg (); | ||
| 138 | msg ("After %d seconds, load average=%d.%02d.", | ||
| 139 | i * 2, load_avg / 100, load_avg % 100); | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | static void | ||
| 144 | load_thread (void *aux UNUSED) | ||
| 145 | { | ||
| 146 | int64_t sleep_time = 10 * TIMER_FREQ; | ||
| 147 | int64_t spin_time = sleep_time + 60 * TIMER_FREQ; | ||
| 148 | int64_t exit_time = spin_time + 60 * TIMER_FREQ; | ||
| 149 | |||
| 150 | thread_set_nice (20); | ||
| 151 | timer_sleep (sleep_time - timer_elapsed (start_time)); | ||
| 152 | while (timer_elapsed (start_time) < spin_time) | ||
| 153 | continue; | ||
| 154 | timer_sleep (exit_time - timer_elapsed (start_time)); | ||
| 155 | } | ||
diff --git a/pintos-progos/tests/threads/mlfqs-load-60.ck b/pintos-progos/tests/threads/mlfqs-load-60.ck new file mode 100644 index 0000000..cb69220 --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-load-60.ck | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::threads::mlfqs; | ||
| 6 | |||
| 7 | our ($test); | ||
| 8 | |||
| 9 | my (@output) = read_text_file ("$test.output"); | ||
| 10 | common_checks ("run", @output); | ||
| 11 | @output = get_core_output ("run", @output); | ||
| 12 | |||
| 13 | # Get actual values. | ||
| 14 | local ($_); | ||
| 15 | my (@actual); | ||
| 16 | foreach (@output) { | ||
| 17 | my ($t, $load_avg) = /After (\d+) seconds, load average=(\d+\.\d+)\./ | ||
| 18 | or next; | ||
| 19 | $actual[$t] = $load_avg; | ||
| 20 | } | ||
| 21 | |||
| 22 | # Calculate expected values. | ||
| 23 | my ($load_avg) = 0; | ||
| 24 | my ($recent) = 0; | ||
| 25 | my (@expected); | ||
| 26 | for (my ($t) = 0; $t < 180; $t++) { | ||
| 27 | my ($ready) = $t < 60 ? 60 : 0; | ||
| 28 | $load_avg = (59/60) * $load_avg + (1/60) * $ready; | ||
| 29 | $expected[$t] = $load_avg; | ||
| 30 | } | ||
| 31 | |||
| 32 | mlfqs_compare ("time", "%.2f", \@actual, \@expected, 3.5, [2, 178, 2], | ||
| 33 | "Some load average values were missing or " | ||
| 34 | . "differed from those expected " | ||
| 35 | . "by more than 3.5."); | ||
| 36 | pass; | ||
diff --git a/pintos-progos/tests/threads/mlfqs-load-avg.c b/pintos-progos/tests/threads/mlfqs-load-avg.c new file mode 100644 index 0000000..50e83e2 --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-load-avg.c | |||
| @@ -0,0 +1,167 @@ | |||
| 1 | /* Starts 60 threads numbered 0 through 59. Thread #i sleeps for | ||
| 2 | (10+i) seconds, then spins in a loop for 60 seconds, then | ||
| 3 | sleeps until a total of 120 seconds have passed. Every 2 | ||
| 4 | seconds, starting 10 seconds in, the main thread prints the | ||
| 5 | load average. | ||
| 6 | |||
| 7 | The expected output is listed below. Some margin of error is | ||
| 8 | allowed. | ||
| 9 | |||
| 10 | If your implementation fails this test but passes most other | ||
| 11 | tests, then consider whether you are doing too much work in | ||
| 12 | the timer interrupt. If the timer interrupt handler takes too | ||
| 13 | long, then the test's main thread will not have enough time to | ||
| 14 | do its own work (printing a message) and go back to sleep | ||
| 15 | before the next tick arrives. Then the main thread will be | ||
| 16 | ready, instead of sleeping, when the tick arrives, | ||
| 17 | artificially driving up the load average. | ||
| 18 | |||
| 19 | After 0 seconds, load average=0.00. | ||
| 20 | After 2 seconds, load average=0.05. | ||
| 21 | After 4 seconds, load average=0.16. | ||
| 22 | After 6 seconds, load average=0.34. | ||
| 23 | After 8 seconds, load average=0.58. | ||
| 24 | After 10 seconds, load average=0.87. | ||
| 25 | After 12 seconds, load average=1.22. | ||
| 26 | After 14 seconds, load average=1.63. | ||
| 27 | After 16 seconds, load average=2.09. | ||
| 28 | After 18 seconds, load average=2.60. | ||
| 29 | After 20 seconds, load average=3.16. | ||
| 30 | After 22 seconds, load average=3.76. | ||
| 31 | After 24 seconds, load average=4.42. | ||
| 32 | After 26 seconds, load average=5.11. | ||
| 33 | After 28 seconds, load average=5.85. | ||
| 34 | After 30 seconds, load average=6.63. | ||
| 35 | After 32 seconds, load average=7.46. | ||
| 36 | After 34 seconds, load average=8.32. | ||
| 37 | After 36 seconds, load average=9.22. | ||
| 38 | After 38 seconds, load average=10.15. | ||
| 39 | After 40 seconds, load average=11.12. | ||
| 40 | After 42 seconds, load average=12.13. | ||
| 41 | After 44 seconds, load average=13.16. | ||
| 42 | After 46 seconds, load average=14.23. | ||
| 43 | After 48 seconds, load average=15.33. | ||
| 44 | After 50 seconds, load average=16.46. | ||
| 45 | After 52 seconds, load average=17.62. | ||
| 46 | After 54 seconds, load average=18.81. | ||
| 47 | After 56 seconds, load average=20.02. | ||
| 48 | After 58 seconds, load average=21.26. | ||
| 49 | After 60 seconds, load average=22.52. | ||
| 50 | After 62 seconds, load average=23.71. | ||
| 51 | After 64 seconds, load average=24.80. | ||
| 52 | After 66 seconds, load average=25.78. | ||
| 53 | After 68 seconds, load average=26.66. | ||
| 54 | After 70 seconds, load average=27.45. | ||
| 55 | After 72 seconds, load average=28.14. | ||
| 56 | After 74 seconds, load average=28.75. | ||
| 57 | After 76 seconds, load average=29.27. | ||
| 58 | After 78 seconds, load average=29.71. | ||
| 59 | After 80 seconds, load average=30.06. | ||
| 60 | After 82 seconds, load average=30.34. | ||
| 61 | After 84 seconds, load average=30.55. | ||
| 62 | After 86 seconds, load average=30.68. | ||
| 63 | After 88 seconds, load average=30.74. | ||
| 64 | After 90 seconds, load average=30.73. | ||
| 65 | After 92 seconds, load average=30.66. | ||
| 66 | After 94 seconds, load average=30.52. | ||
| 67 | After 96 seconds, load average=30.32. | ||
| 68 | After 98 seconds, load average=30.06. | ||
| 69 | After 100 seconds, load average=29.74. | ||
| 70 | After 102 seconds, load average=29.37. | ||
| 71 | After 104 seconds, load average=28.95. | ||
| 72 | After 106 seconds, load average=28.47. | ||
| 73 | After 108 seconds, load average=27.94. | ||
| 74 | After 110 seconds, load average=27.36. | ||
| 75 | After 112 seconds, load average=26.74. | ||
| 76 | After 114 seconds, load average=26.07. | ||
| 77 | After 116 seconds, load average=25.36. | ||
| 78 | After 118 seconds, load average=24.60. | ||
| 79 | After 120 seconds, load average=23.81. | ||
| 80 | After 122 seconds, load average=23.02. | ||
| 81 | After 124 seconds, load average=22.26. | ||
| 82 | After 126 seconds, load average=21.52. | ||
| 83 | After 128 seconds, load average=20.81. | ||
| 84 | After 130 seconds, load average=20.12. | ||
| 85 | After 132 seconds, load average=19.46. | ||
| 86 | After 134 seconds, load average=18.81. | ||
| 87 | After 136 seconds, load average=18.19. | ||
| 88 | After 138 seconds, load average=17.59. | ||
| 89 | After 140 seconds, load average=17.01. | ||
| 90 | After 142 seconds, load average=16.45. | ||
| 91 | After 144 seconds, load average=15.90. | ||
| 92 | After 146 seconds, load average=15.38. | ||
| 93 | After 148 seconds, load average=14.87. | ||
| 94 | After 150 seconds, load average=14.38. | ||
| 95 | After 152 seconds, load average=13.90. | ||
| 96 | After 154 seconds, load average=13.44. | ||
| 97 | After 156 seconds, load average=13.00. | ||
| 98 | After 158 seconds, load average=12.57. | ||
| 99 | After 160 seconds, load average=12.15. | ||
| 100 | After 162 seconds, load average=11.75. | ||
| 101 | After 164 seconds, load average=11.36. | ||
| 102 | After 166 seconds, load average=10.99. | ||
| 103 | After 168 seconds, load average=10.62. | ||
| 104 | After 170 seconds, load average=10.27. | ||
| 105 | After 172 seconds, load average=9.93. | ||
| 106 | After 174 seconds, load average=9.61. | ||
| 107 | After 176 seconds, load average=9.29. | ||
| 108 | After 178 seconds, load average=8.98. | ||
| 109 | */ | ||
| 110 | |||
| 111 | #include <stdio.h> | ||
| 112 | #include "tests/threads/tests.h" | ||
| 113 | #include "threads/init.h" | ||
| 114 | #include "threads/malloc.h" | ||
| 115 | #include "threads/synch.h" | ||
| 116 | #include "threads/thread.h" | ||
| 117 | #include "devices/timer.h" | ||
| 118 | |||
| 119 | static int64_t start_time; | ||
| 120 | |||
| 121 | static void load_thread (void *seq_no); | ||
| 122 | |||
| 123 | #define THREAD_CNT 60 | ||
| 124 | |||
| 125 | void | ||
| 126 | test_mlfqs_load_avg (void) | ||
| 127 | { | ||
| 128 | int i; | ||
| 129 | |||
| 130 | ASSERT (thread_mlfqs); | ||
| 131 | |||
| 132 | start_time = timer_ticks (); | ||
| 133 | msg ("Starting %d load threads...", THREAD_CNT); | ||
| 134 | for (i = 0; i < THREAD_CNT; i++) | ||
| 135 | { | ||
| 136 | char name[16]; | ||
| 137 | snprintf(name, sizeof name, "load %d", i); | ||
| 138 | thread_create (name, PRI_DEFAULT, load_thread, (void *) i); | ||
| 139 | } | ||
| 140 | msg ("Starting threads took %d seconds.", | ||
| 141 | timer_elapsed (start_time) / TIMER_FREQ); | ||
| 142 | thread_set_nice (-20); | ||
| 143 | |||
| 144 | for (i = 0; i < 90; i++) | ||
| 145 | { | ||
| 146 | int64_t sleep_until = start_time + TIMER_FREQ * (2 * i + 10); | ||
| 147 | int load_avg; | ||
| 148 | timer_sleep (sleep_until - timer_ticks ()); | ||
| 149 | load_avg = thread_get_load_avg (); | ||
| 150 | msg ("After %d seconds, load average=%d.%02d.", | ||
| 151 | i * 2, load_avg / 100, load_avg % 100); | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | static void | ||
| 156 | load_thread (void *seq_no_) | ||
| 157 | { | ||
| 158 | int seq_no = (int) seq_no_; | ||
| 159 | int sleep_time = TIMER_FREQ * (10 + seq_no); | ||
| 160 | int spin_time = sleep_time + TIMER_FREQ * THREAD_CNT; | ||
| 161 | int exit_time = TIMER_FREQ * (THREAD_CNT * 2); | ||
| 162 | |||
| 163 | timer_sleep (sleep_time - timer_elapsed (start_time)); | ||
| 164 | while (timer_elapsed (start_time) < spin_time) | ||
| 165 | continue; | ||
| 166 | timer_sleep (exit_time - timer_elapsed (start_time)); | ||
| 167 | } | ||
diff --git a/pintos-progos/tests/threads/mlfqs-load-avg.ck b/pintos-progos/tests/threads/mlfqs-load-avg.ck new file mode 100644 index 0000000..2254d05 --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-load-avg.ck | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::threads::mlfqs; | ||
| 6 | |||
| 7 | our ($test); | ||
| 8 | my (@output) = read_text_file ("$test.output"); | ||
| 9 | |||
| 10 | common_checks ("run", @output); | ||
| 11 | @output = get_core_output ("run", @output); | ||
| 12 | |||
| 13 | # Get actual values. | ||
| 14 | local ($_); | ||
| 15 | my (@actual); | ||
| 16 | foreach (@output) { | ||
| 17 | my ($t, $load_avg) = /After (\d+) seconds, load average=(\d+\.\d+)\./ | ||
| 18 | or next; | ||
| 19 | $actual[$t] = $load_avg; | ||
| 20 | } | ||
| 21 | |||
| 22 | # Calculate expected values. | ||
| 23 | my ($load_avg) = 0; | ||
| 24 | my ($recent) = 0; | ||
| 25 | my (@expected); | ||
| 26 | for (my ($t) = 0; $t < 180; $t++) { | ||
| 27 | my ($ready) = $t < 60 ? $t : $t < 120 ? 120 - $t : 0; | ||
| 28 | $load_avg = (59/60) * $load_avg + (1/60) * $ready; | ||
| 29 | $expected[$t] = $load_avg; | ||
| 30 | } | ||
| 31 | |||
| 32 | mlfqs_compare ("time", "%.2f", \@actual, \@expected, 2.5, [2, 178, 2], | ||
| 33 | "Some load average values were missing or " | ||
| 34 | . "differed from those expected " | ||
| 35 | . "by more than 2.5."); | ||
| 36 | pass; | ||
diff --git a/pintos-progos/tests/threads/mlfqs-nice-10.ck b/pintos-progos/tests/threads/mlfqs-nice-10.ck new file mode 100644 index 0000000..53e0abe --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-nice-10.ck | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::threads::mlfqs; | ||
| 6 | |||
| 7 | check_mlfqs_fair ([0...9], 25); | ||
diff --git a/pintos-progos/tests/threads/mlfqs-nice-2.ck b/pintos-progos/tests/threads/mlfqs-nice-2.ck new file mode 100644 index 0000000..ada366b --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-nice-2.ck | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::threads::mlfqs; | ||
| 6 | |||
| 7 | check_mlfqs_fair ([0, 5], 50); | ||
diff --git a/pintos-progos/tests/threads/mlfqs-recent-1.c b/pintos-progos/tests/threads/mlfqs-recent-1.c new file mode 100644 index 0000000..4258671 --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-recent-1.c | |||
| @@ -0,0 +1,144 @@ | |||
| 1 | /* Checks that recent_cpu is calculated properly for the case of | ||
| 2 | a single ready process. | ||
| 3 | |||
| 4 | The expected output is this (some margin of error is allowed): | ||
| 5 | |||
| 6 | After 2 seconds, recent_cpu is 6.40, load_avg is 0.03. | ||
| 7 | After 4 seconds, recent_cpu is 12.60, load_avg is 0.07. | ||
| 8 | After 6 seconds, recent_cpu is 18.61, load_avg is 0.10. | ||
| 9 | After 8 seconds, recent_cpu is 24.44, load_avg is 0.13. | ||
| 10 | After 10 seconds, recent_cpu is 30.08, load_avg is 0.15. | ||
| 11 | After 12 seconds, recent_cpu is 35.54, load_avg is 0.18. | ||
| 12 | After 14 seconds, recent_cpu is 40.83, load_avg is 0.21. | ||
| 13 | After 16 seconds, recent_cpu is 45.96, load_avg is 0.24. | ||
| 14 | After 18 seconds, recent_cpu is 50.92, load_avg is 0.26. | ||
| 15 | After 20 seconds, recent_cpu is 55.73, load_avg is 0.29. | ||
| 16 | After 22 seconds, recent_cpu is 60.39, load_avg is 0.31. | ||
| 17 | After 24 seconds, recent_cpu is 64.90, load_avg is 0.33. | ||
| 18 | After 26 seconds, recent_cpu is 69.27, load_avg is 0.35. | ||
| 19 | After 28 seconds, recent_cpu is 73.50, load_avg is 0.38. | ||
| 20 | After 30 seconds, recent_cpu is 77.60, load_avg is 0.40. | ||
| 21 | After 32 seconds, recent_cpu is 81.56, load_avg is 0.42. | ||
| 22 | After 34 seconds, recent_cpu is 85.40, load_avg is 0.44. | ||
| 23 | After 36 seconds, recent_cpu is 89.12, load_avg is 0.45. | ||
| 24 | After 38 seconds, recent_cpu is 92.72, load_avg is 0.47. | ||
| 25 | After 40 seconds, recent_cpu is 96.20, load_avg is 0.49. | ||
| 26 | After 42 seconds, recent_cpu is 99.57, load_avg is 0.51. | ||
| 27 | After 44 seconds, recent_cpu is 102.84, load_avg is 0.52. | ||
| 28 | After 46 seconds, recent_cpu is 106.00, load_avg is 0.54. | ||
| 29 | After 48 seconds, recent_cpu is 109.06, load_avg is 0.55. | ||
| 30 | After 50 seconds, recent_cpu is 112.02, load_avg is 0.57. | ||
| 31 | After 52 seconds, recent_cpu is 114.89, load_avg is 0.58. | ||
| 32 | After 54 seconds, recent_cpu is 117.66, load_avg is 0.60. | ||
| 33 | After 56 seconds, recent_cpu is 120.34, load_avg is 0.61. | ||
| 34 | After 58 seconds, recent_cpu is 122.94, load_avg is 0.62. | ||
| 35 | After 60 seconds, recent_cpu is 125.46, load_avg is 0.64. | ||
| 36 | After 62 seconds, recent_cpu is 127.89, load_avg is 0.65. | ||
| 37 | After 64 seconds, recent_cpu is 130.25, load_avg is 0.66. | ||
| 38 | After 66 seconds, recent_cpu is 132.53, load_avg is 0.67. | ||
| 39 | After 68 seconds, recent_cpu is 134.73, load_avg is 0.68. | ||
| 40 | After 70 seconds, recent_cpu is 136.86, load_avg is 0.69. | ||
| 41 | After 72 seconds, recent_cpu is 138.93, load_avg is 0.70. | ||
| 42 | After 74 seconds, recent_cpu is 140.93, load_avg is 0.71. | ||
| 43 | After 76 seconds, recent_cpu is 142.86, load_avg is 0.72. | ||
| 44 | After 78 seconds, recent_cpu is 144.73, load_avg is 0.73. | ||
| 45 | After 80 seconds, recent_cpu is 146.54, load_avg is 0.74. | ||
| 46 | After 82 seconds, recent_cpu is 148.29, load_avg is 0.75. | ||
| 47 | After 84 seconds, recent_cpu is 149.99, load_avg is 0.76. | ||
| 48 | After 86 seconds, recent_cpu is 151.63, load_avg is 0.76. | ||
| 49 | After 88 seconds, recent_cpu is 153.21, load_avg is 0.77. | ||
| 50 | After 90 seconds, recent_cpu is 154.75, load_avg is 0.78. | ||
| 51 | After 92 seconds, recent_cpu is 156.23, load_avg is 0.79. | ||
| 52 | After 94 seconds, recent_cpu is 157.67, load_avg is 0.79. | ||
| 53 | After 96 seconds, recent_cpu is 159.06, load_avg is 0.80. | ||
| 54 | After 98 seconds, recent_cpu is 160.40, load_avg is 0.81. | ||
| 55 | After 100 seconds, recent_cpu is 161.70, load_avg is 0.81. | ||
| 56 | After 102 seconds, recent_cpu is 162.96, load_avg is 0.82. | ||
| 57 | After 104 seconds, recent_cpu is 164.18, load_avg is 0.83. | ||
| 58 | After 106 seconds, recent_cpu is 165.35, load_avg is 0.83. | ||
| 59 | After 108 seconds, recent_cpu is 166.49, load_avg is 0.84. | ||
| 60 | After 110 seconds, recent_cpu is 167.59, load_avg is 0.84. | ||
| 61 | After 112 seconds, recent_cpu is 168.66, load_avg is 0.85. | ||
| 62 | After 114 seconds, recent_cpu is 169.69, load_avg is 0.85. | ||
| 63 | After 116 seconds, recent_cpu is 170.69, load_avg is 0.86. | ||
| 64 | After 118 seconds, recent_cpu is 171.65, load_avg is 0.86. | ||
| 65 | After 120 seconds, recent_cpu is 172.58, load_avg is 0.87. | ||
| 66 | After 122 seconds, recent_cpu is 173.49, load_avg is 0.87. | ||
| 67 | After 124 seconds, recent_cpu is 174.36, load_avg is 0.88. | ||
| 68 | After 126 seconds, recent_cpu is 175.20, load_avg is 0.88. | ||
| 69 | After 128 seconds, recent_cpu is 176.02, load_avg is 0.88. | ||
| 70 | After 130 seconds, recent_cpu is 176.81, load_avg is 0.89. | ||
| 71 | After 132 seconds, recent_cpu is 177.57, load_avg is 0.89. | ||
| 72 | After 134 seconds, recent_cpu is 178.31, load_avg is 0.89. | ||
| 73 | After 136 seconds, recent_cpu is 179.02, load_avg is 0.90. | ||
| 74 | After 138 seconds, recent_cpu is 179.72, load_avg is 0.90. | ||
| 75 | After 140 seconds, recent_cpu is 180.38, load_avg is 0.90. | ||
| 76 | After 142 seconds, recent_cpu is 181.03, load_avg is 0.91. | ||
| 77 | After 144 seconds, recent_cpu is 181.65, load_avg is 0.91. | ||
| 78 | After 146 seconds, recent_cpu is 182.26, load_avg is 0.91. | ||
| 79 | After 148 seconds, recent_cpu is 182.84, load_avg is 0.92. | ||
| 80 | After 150 seconds, recent_cpu is 183.41, load_avg is 0.92. | ||
| 81 | After 152 seconds, recent_cpu is 183.96, load_avg is 0.92. | ||
| 82 | After 154 seconds, recent_cpu is 184.49, load_avg is 0.92. | ||
| 83 | After 156 seconds, recent_cpu is 185.00, load_avg is 0.93. | ||
| 84 | After 158 seconds, recent_cpu is 185.49, load_avg is 0.93. | ||
| 85 | After 160 seconds, recent_cpu is 185.97, load_avg is 0.93. | ||
| 86 | After 162 seconds, recent_cpu is 186.43, load_avg is 0.93. | ||
| 87 | After 164 seconds, recent_cpu is 186.88, load_avg is 0.94. | ||
| 88 | After 166 seconds, recent_cpu is 187.31, load_avg is 0.94. | ||
| 89 | After 168 seconds, recent_cpu is 187.73, load_avg is 0.94. | ||
| 90 | After 170 seconds, recent_cpu is 188.14, load_avg is 0.94. | ||
| 91 | After 172 seconds, recent_cpu is 188.53, load_avg is 0.94. | ||
| 92 | After 174 seconds, recent_cpu is 188.91, load_avg is 0.95. | ||
| 93 | After 176 seconds, recent_cpu is 189.27, load_avg is 0.95. | ||
| 94 | After 178 seconds, recent_cpu is 189.63, load_avg is 0.95. | ||
| 95 | After 180 seconds, recent_cpu is 189.97, load_avg is 0.95. | ||
| 96 | */ | ||
| 97 | |||
| 98 | #include <stdio.h> | ||
| 99 | #include "tests/threads/tests.h" | ||
| 100 | #include "threads/init.h" | ||
| 101 | #include "threads/malloc.h" | ||
| 102 | #include "threads/synch.h" | ||
| 103 | #include "threads/thread.h" | ||
| 104 | #include "devices/timer.h" | ||
| 105 | |||
| 106 | /* Sensitive to assumption that recent_cpu updates happen exactly | ||
| 107 | when timer_ticks() % TIMER_FREQ == 0. */ | ||
| 108 | |||
| 109 | void | ||
| 110 | test_mlfqs_recent_1 (void) | ||
| 111 | { | ||
| 112 | int64_t start_time; | ||
| 113 | int last_elapsed = 0; | ||
| 114 | |||
| 115 | ASSERT (thread_mlfqs); | ||
| 116 | |||
| 117 | do | ||
| 118 | { | ||
| 119 | msg ("Sleeping 10 seconds to allow recent_cpu to decay, please wait..."); | ||
| 120 | start_time = timer_ticks (); | ||
| 121 | timer_sleep (DIV_ROUND_UP (start_time, TIMER_FREQ) - start_time | ||
| 122 | + 10 * TIMER_FREQ); | ||
| 123 | } | ||
| 124 | while (thread_get_recent_cpu () > 700); | ||
| 125 | |||
| 126 | start_time = timer_ticks (); | ||
| 127 | for (;;) | ||
| 128 | { | ||
| 129 | int elapsed = timer_elapsed (start_time); | ||
| 130 | if (elapsed % (TIMER_FREQ * 2) == 0 && elapsed > last_elapsed) | ||
| 131 | { | ||
| 132 | int recent_cpu = thread_get_recent_cpu (); | ||
| 133 | int load_avg = thread_get_load_avg (); | ||
| 134 | int elapsed_seconds = elapsed / TIMER_FREQ; | ||
| 135 | msg ("After %d seconds, recent_cpu is %d.%02d, load_avg is %d.%02d.", | ||
| 136 | elapsed_seconds, | ||
| 137 | recent_cpu / 100, recent_cpu % 100, | ||
| 138 | load_avg / 100, load_avg % 100); | ||
| 139 | if (elapsed_seconds >= 180) | ||
| 140 | break; | ||
| 141 | } | ||
| 142 | last_elapsed = elapsed; | ||
| 143 | } | ||
| 144 | } | ||
diff --git a/pintos-progos/tests/threads/mlfqs-recent-1.ck b/pintos-progos/tests/threads/mlfqs-recent-1.ck new file mode 100644 index 0000000..a2ba44d --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-recent-1.ck | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::threads::mlfqs; | ||
| 6 | |||
| 7 | our ($test); | ||
| 8 | my (@output) = read_text_file ("$test.output"); | ||
| 9 | common_checks ("run", @output); | ||
| 10 | @output = get_core_output ("run", @output); | ||
| 11 | |||
| 12 | # Get actual values. | ||
| 13 | local ($_); | ||
| 14 | my (@actual); | ||
| 15 | foreach (@output) { | ||
| 16 | my ($t, $recent_cpu) = /After (\d+) seconds, recent_cpu is (\d+\.\d+),/ | ||
| 17 | or next; | ||
| 18 | $actual[$t] = $recent_cpu; | ||
| 19 | } | ||
| 20 | |||
| 21 | # Calculate expected values. | ||
| 22 | my ($expected_load_avg, $expected_recent_cpu) | ||
| 23 | = mlfqs_expected_load ([(1) x 180], [(100) x 180]); | ||
| 24 | my (@expected) = @$expected_recent_cpu; | ||
| 25 | |||
| 26 | # Compare actual and expected values. | ||
| 27 | mlfqs_compare ("time", "%.2f", \@actual, \@expected, 2.5, [2, 178, 2], | ||
| 28 | "Some recent_cpu values were missing or " | ||
| 29 | . "differed from those expected " | ||
| 30 | . "by more than 2.5."); | ||
| 31 | pass; | ||
diff --git a/pintos-progos/tests/threads/mlfqs.pm b/pintos-progos/tests/threads/mlfqs.pm new file mode 100644 index 0000000..184ac16 --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs.pm | |||
| @@ -0,0 +1,146 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | |||
| 5 | sub mlfqs_expected_load { | ||
| 6 | my ($ready, $recent_delta) = @_; | ||
| 7 | my (@load_avg) = 0; | ||
| 8 | my (@recent_cpu) = 0; | ||
| 9 | my ($load_avg) = 0; | ||
| 10 | my ($recent_cpu) = 0; | ||
| 11 | for my $i (0...$#$ready) { | ||
| 12 | $load_avg = (59/60) * $load_avg + (1/60) * $ready->[$i]; | ||
| 13 | push (@load_avg, $load_avg); | ||
| 14 | |||
| 15 | if (defined $recent_delta->[$i]) { | ||
| 16 | my ($twice_load) = $load_avg * 2; | ||
| 17 | my ($load_factor) = $twice_load / ($twice_load + 1); | ||
| 18 | $recent_cpu = ($recent_cpu + $recent_delta->[$i]) * $load_factor; | ||
| 19 | push (@recent_cpu, $recent_cpu); | ||
| 20 | } | ||
| 21 | } | ||
| 22 | return (\@load_avg, \@recent_cpu); | ||
| 23 | } | ||
| 24 | |||
| 25 | sub mlfqs_expected_ticks { | ||
| 26 | my (@nice) = @_; | ||
| 27 | my ($thread_cnt) = scalar (@nice); | ||
| 28 | my (@recent_cpu) = (0) x $thread_cnt; | ||
| 29 | my (@slices) = (0) x $thread_cnt; | ||
| 30 | my (@fifo) = (0) x $thread_cnt; | ||
| 31 | my ($next_fifo) = 1; | ||
| 32 | my ($load_avg) = 0; | ||
| 33 | for my $i (1...750) { | ||
| 34 | if ($i % 25 == 0) { | ||
| 35 | # Update load average. | ||
| 36 | $load_avg = (59/60) * $load_avg + (1/60) * $thread_cnt; | ||
| 37 | |||
| 38 | # Update recent_cpu. | ||
| 39 | my ($twice_load) = $load_avg * 2; | ||
| 40 | my ($load_factor) = $twice_load / ($twice_load + 1); | ||
| 41 | $recent_cpu[$_] = $recent_cpu[$_] * $load_factor + $nice[$_] | ||
| 42 | foreach 0...($thread_cnt - 1); | ||
| 43 | } | ||
| 44 | |||
| 45 | # Update priorities. | ||
| 46 | my (@priority); | ||
| 47 | foreach my $j (0...($thread_cnt - 1)) { | ||
| 48 | my ($priority) = int ($recent_cpu[$j] / 4 + $nice[$j] * 2); | ||
| 49 | $priority = 0 if $priority < 0; | ||
| 50 | $priority = 63 if $priority > 63; | ||
| 51 | push (@priority, $priority); | ||
| 52 | } | ||
| 53 | |||
| 54 | # Choose thread to run. | ||
| 55 | my $max = 0; | ||
| 56 | for my $j (1...$#priority) { | ||
| 57 | if ($priority[$j] < $priority[$max] | ||
| 58 | || ($priority[$j] == $priority[$max] | ||
| 59 | && $fifo[$j] < $fifo[$max])) { | ||
| 60 | $max = $j; | ||
| 61 | } | ||
| 62 | } | ||
| 63 | $fifo[$max] = $next_fifo++; | ||
| 64 | |||
| 65 | # Run thread. | ||
| 66 | $recent_cpu[$max] += 4; | ||
| 67 | $slices[$max] += 4; | ||
| 68 | } | ||
| 69 | return @slices; | ||
| 70 | } | ||
| 71 | |||
| 72 | sub check_mlfqs_fair { | ||
| 73 | my ($nice, $maxdiff) = @_; | ||
| 74 | our ($test); | ||
| 75 | my (@output) = read_text_file ("$test.output"); | ||
| 76 | common_checks ("run", @output); | ||
| 77 | @output = get_core_output ("run", @output); | ||
| 78 | |||
| 79 | my (@actual); | ||
| 80 | local ($_); | ||
| 81 | foreach (@output) { | ||
| 82 | my ($id, $count) = /Thread (\d+) received (\d+) ticks\./ or next; | ||
| 83 | $actual[$id] = $count; | ||
| 84 | } | ||
| 85 | |||
| 86 | my (@expected) = mlfqs_expected_ticks (@$nice); | ||
| 87 | mlfqs_compare ("thread", "%d", | ||
| 88 | \@actual, \@expected, $maxdiff, [0, $#$nice, 1], | ||
| 89 | "Some tick counts were missing or differed from those " | ||
| 90 | . "expected by more than $maxdiff."); | ||
| 91 | pass; | ||
| 92 | } | ||
| 93 | |||
| 94 | sub mlfqs_compare { | ||
| 95 | my ($indep_var, $format, | ||
| 96 | $actual_ref, $expected_ref, $maxdiff, $t_range, $message) = @_; | ||
| 97 | my ($t_min, $t_max, $t_step) = @$t_range; | ||
| 98 | |||
| 99 | my ($ok) = 1; | ||
| 100 | for (my ($t) = $t_min; $t <= $t_max; $t += $t_step) { | ||
| 101 | my ($actual) = $actual_ref->[$t]; | ||
| 102 | my ($expected) = $expected_ref->[$t]; | ||
| 103 | $ok = 0, last | ||
| 104 | if !defined ($actual) || abs ($actual - $expected) > $maxdiff + .01; | ||
| 105 | } | ||
| 106 | return if $ok; | ||
| 107 | |||
| 108 | print "$message\n"; | ||
| 109 | mlfqs_row ($indep_var, "actual", "<->", "expected", "explanation"); | ||
| 110 | mlfqs_row ("------", "--------", "---", "--------", '-' x 40); | ||
| 111 | for (my ($t) = $t_min; $t <= $t_max; $t += $t_step) { | ||
| 112 | my ($actual) = $actual_ref->[$t]; | ||
| 113 | my ($expected) = $expected_ref->[$t]; | ||
| 114 | my ($diff, $rationale); | ||
| 115 | if (!defined $actual) { | ||
| 116 | $actual = 'undef' ; | ||
| 117 | $diff = ''; | ||
| 118 | $rationale = 'Missing value.'; | ||
| 119 | } else { | ||
| 120 | my ($delta) = abs ($actual - $expected); | ||
| 121 | if ($delta > $maxdiff + .01) { | ||
| 122 | my ($excess) = $delta - $maxdiff; | ||
| 123 | if ($actual > $expected) { | ||
| 124 | $diff = '>>>'; | ||
| 125 | $rationale = sprintf "Too big, by $format.", $excess; | ||
| 126 | } else { | ||
| 127 | $diff = '<<<'; | ||
| 128 | $rationale = sprintf "Too small, by $format.", $excess; | ||
| 129 | } | ||
| 130 | } else { | ||
| 131 | $diff = ' = '; | ||
| 132 | $rationale = ''; | ||
| 133 | } | ||
| 134 | $actual = sprintf ($format, $actual); | ||
| 135 | } | ||
| 136 | $expected = sprintf ($format, $expected); | ||
| 137 | mlfqs_row ($t, $actual, $diff, $expected, $rationale); | ||
| 138 | } | ||
| 139 | fail; | ||
| 140 | } | ||
| 141 | |||
| 142 | sub mlfqs_row { | ||
| 143 | printf "%6s %8s %3s %-8s %s\n", @_; | ||
| 144 | } | ||
| 145 | |||
| 146 | 1; | ||
diff --git a/pintos-progos/tests/threads/priority-change.c b/pintos-progos/tests/threads/priority-change.c new file mode 100644 index 0000000..810b05a --- /dev/null +++ b/pintos-progos/tests/threads/priority-change.c | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | /* Verifies that lowering a thread's priority so that it is no | ||
| 2 | longer the highest-priority thread in the system causes it to | ||
| 3 | yield immediately. */ | ||
| 4 | |||
| 5 | #include <stdio.h> | ||
| 6 | #include "tests/threads/tests.h" | ||
| 7 | #include "threads/init.h" | ||
| 8 | #include "threads/thread.h" | ||
| 9 | |||
| 10 | static thread_func changing_thread; | ||
| 11 | |||
| 12 | void | ||
| 13 | test_priority_change (void) | ||
| 14 | { | ||
| 15 | /* This test does not work with the MLFQS. */ | ||
| 16 | ASSERT (!thread_mlfqs); | ||
| 17 | |||
| 18 | msg ("Creating a high-priority thread 2."); | ||
| 19 | thread_create ("thread 2", PRI_DEFAULT + 1, changing_thread, NULL); | ||
| 20 | msg ("Thread 2 should have just lowered its priority."); | ||
| 21 | thread_set_priority (PRI_DEFAULT - 2); | ||
| 22 | msg ("Thread 2 should have just exited."); | ||
| 23 | } | ||
| 24 | |||
| 25 | static void | ||
| 26 | changing_thread (void *aux UNUSED) | ||
| 27 | { | ||
| 28 | msg ("Thread 2 now lowering priority."); | ||
| 29 | thread_set_priority (PRI_DEFAULT - 1); | ||
| 30 | msg ("Thread 2 exiting."); | ||
| 31 | } | ||
diff --git a/pintos-progos/tests/threads/priority-change.ck b/pintos-progos/tests/threads/priority-change.ck new file mode 100644 index 0000000..f4d9b2f --- /dev/null +++ b/pintos-progos/tests/threads/priority-change.ck | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (priority-change) begin | ||
| 7 | (priority-change) Creating a high-priority thread 2. | ||
| 8 | (priority-change) Thread 2 now lowering priority. | ||
| 9 | (priority-change) Thread 2 should have just lowered its priority. | ||
| 10 | (priority-change) Thread 2 exiting. | ||
| 11 | (priority-change) Thread 2 should have just exited. | ||
| 12 | (priority-change) end | ||
| 13 | EOF | ||
| 14 | pass; | ||
diff --git a/pintos-progos/tests/threads/priority-condvar.c b/pintos-progos/tests/threads/priority-condvar.c new file mode 100644 index 0000000..c1efb1b --- /dev/null +++ b/pintos-progos/tests/threads/priority-condvar.c | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | /* Tests that cond_signal() wakes up the highest-priority thread | ||
| 2 | waiting in cond_wait(). */ | ||
| 3 | |||
| 4 | #include <stdio.h> | ||
| 5 | #include "tests/threads/tests.h" | ||
| 6 | #include "threads/init.h" | ||
| 7 | #include "threads/malloc.h" | ||
| 8 | #include "threads/synch.h" | ||
| 9 | #include "threads/thread.h" | ||
| 10 | #include "devices/timer.h" | ||
| 11 | |||
| 12 | static thread_func priority_condvar_thread; | ||
| 13 | static struct lock lock; | ||
| 14 | static struct condition condition; | ||
| 15 | |||
| 16 | void | ||
| 17 | test_priority_condvar (void) | ||
| 18 | { | ||
| 19 | int i; | ||
| 20 | |||
| 21 | /* This test does not work with the MLFQS. */ | ||
| 22 | ASSERT (!thread_mlfqs); | ||
| 23 | |||
| 24 | lock_init (&lock); | ||
| 25 | cond_init (&condition); | ||
| 26 | |||
| 27 | thread_set_priority (PRI_MIN); | ||
| 28 | for (i = 0; i < 10; i++) | ||
| 29 | { | ||
| 30 | int priority = PRI_DEFAULT - (i + 7) % 10 - 1; | ||
| 31 | char name[16]; | ||
| 32 | snprintf (name, sizeof name, "priority %d", priority); | ||
| 33 | thread_create (name, priority, priority_condvar_thread, NULL); | ||
| 34 | } | ||
| 35 | |||
| 36 | for (i = 0; i < 10; i++) | ||
| 37 | { | ||
| 38 | lock_acquire (&lock); | ||
| 39 | msg ("Signaling..."); | ||
| 40 | cond_signal (&condition, &lock); | ||
| 41 | lock_release (&lock); | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | static void | ||
| 46 | priority_condvar_thread (void *aux UNUSED) | ||
| 47 | { | ||
| 48 | msg ("Thread %s starting.", thread_name ()); | ||
| 49 | lock_acquire (&lock); | ||
| 50 | cond_wait (&condition, &lock); | ||
| 51 | msg ("Thread %s woke up.", thread_name ()); | ||
| 52 | lock_release (&lock); | ||
| 53 | } | ||
diff --git a/pintos-progos/tests/threads/priority-condvar.ck b/pintos-progos/tests/threads/priority-condvar.ck new file mode 100644 index 0000000..195c1ab --- /dev/null +++ b/pintos-progos/tests/threads/priority-condvar.ck | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (priority-condvar) begin | ||
| 7 | (priority-condvar) Thread priority 23 starting. | ||
| 8 | (priority-condvar) Thread priority 22 starting. | ||
| 9 | (priority-condvar) Thread priority 21 starting. | ||
| 10 | (priority-condvar) Thread priority 30 starting. | ||
| 11 | (priority-condvar) Thread priority 29 starting. | ||
| 12 | (priority-condvar) Thread priority 28 starting. | ||
| 13 | (priority-condvar) Thread priority 27 starting. | ||
| 14 | (priority-condvar) Thread priority 26 starting. | ||
| 15 | (priority-condvar) Thread priority 25 starting. | ||
| 16 | (priority-condvar) Thread priority 24 starting. | ||
| 17 | (priority-condvar) Signaling... | ||
| 18 | (priority-condvar) Thread priority 30 woke up. | ||
| 19 | (priority-condvar) Signaling... | ||
| 20 | (priority-condvar) Thread priority 29 woke up. | ||
| 21 | (priority-condvar) Signaling... | ||
| 22 | (priority-condvar) Thread priority 28 woke up. | ||
| 23 | (priority-condvar) Signaling... | ||
| 24 | (priority-condvar) Thread priority 27 woke up. | ||
| 25 | (priority-condvar) Signaling... | ||
| 26 | (priority-condvar) Thread priority 26 woke up. | ||
| 27 | (priority-condvar) Signaling... | ||
| 28 | (priority-condvar) Thread priority 25 woke up. | ||
| 29 | (priority-condvar) Signaling... | ||
| 30 | (priority-condvar) Thread priority 24 woke up. | ||
| 31 | (priority-condvar) Signaling... | ||
| 32 | (priority-condvar) Thread priority 23 woke up. | ||
| 33 | (priority-condvar) Signaling... | ||
| 34 | (priority-condvar) Thread priority 22 woke up. | ||
| 35 | (priority-condvar) Signaling... | ||
| 36 | (priority-condvar) Thread priority 21 woke up. | ||
| 37 | (priority-condvar) end | ||
| 38 | EOF | ||
| 39 | pass; | ||
diff --git a/pintos-progos/tests/threads/priority-donate-chain.c b/pintos-progos/tests/threads/priority-donate-chain.c new file mode 100644 index 0000000..3ffabca --- /dev/null +++ b/pintos-progos/tests/threads/priority-donate-chain.c | |||
| @@ -0,0 +1,114 @@ | |||
| 1 | /* The main thread set its priority to PRI_MIN and creates 7 threads | ||
| 2 | (thread 1..7) with priorities PRI_MIN + 3, 6, 9, 12, ... | ||
| 3 | The main thread initializes 8 locks: lock 0..7 and acquires lock 0. | ||
| 4 | |||
| 5 | When thread[i] starts, it first acquires lock[i] (unless i == 7.) | ||
| 6 | Subsequently, thread[i] attempts to acquire lock[i-1], which is held by | ||
| 7 | thread[i-1], except for lock[0], which is held by the main thread. | ||
| 8 | Because the lock is held, thread[i] donates its priority to thread[i-1], | ||
| 9 | which donates to thread[i-2], and so on until the main thread | ||
| 10 | receives the donation. | ||
| 11 | |||
| 12 | After threads[1..7] have been created and are blocked on locks[0..7], | ||
| 13 | the main thread releases lock[0], unblocking thread[1], and being | ||
| 14 | preempted by it. | ||
| 15 | Thread[1] then completes acquiring lock[0], then releases lock[0], | ||
| 16 | then releases lock[1], unblocking thread[2], etc. | ||
| 17 | Thread[7] finally acquires & releases lock[7] and exits, allowing | ||
| 18 | thread[6], then thread[5] etc. to run and exit until finally the | ||
| 19 | main thread exits. | ||
| 20 | |||
| 21 | In addition, interloper threads are created at priority levels | ||
| 22 | p = PRI_MIN + 2, 5, 8, 11, ... which should not be run until the | ||
| 23 | corresponding thread with priority p + 1 has finished. | ||
| 24 | |||
| 25 | Written by Godmar Back <gback@cs.vt.edu> */ | ||
| 26 | |||
| 27 | #include <stdio.h> | ||
| 28 | #include "tests/threads/tests.h" | ||
| 29 | #include "threads/init.h" | ||
| 30 | #include "threads/synch.h" | ||
| 31 | #include "threads/thread.h" | ||
| 32 | |||
| 33 | #define NESTING_DEPTH 8 | ||
| 34 | |||
| 35 | struct lock_pair | ||
| 36 | { | ||
| 37 | struct lock *second; | ||
| 38 | struct lock *first; | ||
| 39 | }; | ||
| 40 | |||
| 41 | static thread_func donor_thread_func; | ||
| 42 | static thread_func interloper_thread_func; | ||
| 43 | |||
| 44 | void | ||
| 45 | test_priority_donate_chain (void) | ||
| 46 | { | ||
| 47 | int i; | ||
| 48 | struct lock locks[NESTING_DEPTH - 1]; | ||
| 49 | struct lock_pair lock_pairs[NESTING_DEPTH]; | ||
| 50 | |||
| 51 | /* This test does not work with the MLFQS. */ | ||
| 52 | ASSERT (!thread_mlfqs); | ||
| 53 | |||
| 54 | thread_set_priority (PRI_MIN); | ||
| 55 | |||
| 56 | for (i = 0; i < NESTING_DEPTH - 1; i++) | ||
| 57 | lock_init (&locks[i]); | ||
| 58 | |||
| 59 | lock_acquire (&locks[0]); | ||
| 60 | msg ("%s got lock.", thread_name ()); | ||
| 61 | |||
| 62 | for (i = 1; i < NESTING_DEPTH; i++) | ||
| 63 | { | ||
| 64 | char name[16]; | ||
| 65 | int thread_priority; | ||
| 66 | |||
| 67 | snprintf (name, sizeof name, "thread %d", i); | ||
| 68 | thread_priority = PRI_MIN + i * 3; | ||
| 69 | lock_pairs[i].first = i < NESTING_DEPTH - 1 ? locks + i: NULL; | ||
| 70 | lock_pairs[i].second = locks + i - 1; | ||
| 71 | |||
| 72 | thread_create (name, thread_priority, donor_thread_func, lock_pairs + i); | ||
| 73 | msg ("%s should have priority %d. Actual priority: %d.", | ||
| 74 | thread_name (), thread_priority, thread_get_priority ()); | ||
| 75 | |||
| 76 | snprintf (name, sizeof name, "interloper %d", i); | ||
| 77 | thread_create (name, thread_priority - 1, interloper_thread_func, NULL); | ||
| 78 | } | ||
| 79 | |||
| 80 | lock_release (&locks[0]); | ||
| 81 | msg ("%s finishing with priority %d.", thread_name (), | ||
| 82 | thread_get_priority ()); | ||
| 83 | } | ||
| 84 | |||
| 85 | static void | ||
| 86 | donor_thread_func (void *locks_) | ||
| 87 | { | ||
| 88 | struct lock_pair *locks = locks_; | ||
| 89 | |||
| 90 | if (locks->first) | ||
| 91 | lock_acquire (locks->first); | ||
| 92 | |||
| 93 | lock_acquire (locks->second); | ||
| 94 | msg ("%s got lock", thread_name ()); | ||
| 95 | |||
| 96 | lock_release (locks->second); | ||
| 97 | msg ("%s should have priority %d. Actual priority: %d", | ||
| 98 | thread_name (), (NESTING_DEPTH - 1) * 3, | ||
| 99 | thread_get_priority ()); | ||
| 100 | |||
| 101 | if (locks->first) | ||
| 102 | lock_release (locks->first); | ||
| 103 | |||
| 104 | msg ("%s finishing with priority %d.", thread_name (), | ||
| 105 | thread_get_priority ()); | ||
| 106 | } | ||
| 107 | |||
| 108 | static void | ||
| 109 | interloper_thread_func (void *arg_ UNUSED) | ||
| 110 | { | ||
| 111 | msg ("%s finished.", thread_name ()); | ||
| 112 | } | ||
| 113 | |||
| 114 | // vim: sw=2 | ||
diff --git a/pintos-progos/tests/threads/priority-donate-chain.ck b/pintos-progos/tests/threads/priority-donate-chain.ck new file mode 100644 index 0000000..213e649 --- /dev/null +++ b/pintos-progos/tests/threads/priority-donate-chain.ck | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (priority-donate-chain) begin | ||
| 7 | (priority-donate-chain) main got lock. | ||
| 8 | (priority-donate-chain) main should have priority 3. Actual priority: 3. | ||
| 9 | (priority-donate-chain) main should have priority 6. Actual priority: 6. | ||
| 10 | (priority-donate-chain) main should have priority 9. Actual priority: 9. | ||
| 11 | (priority-donate-chain) main should have priority 12. Actual priority: 12. | ||
| 12 | (priority-donate-chain) main should have priority 15. Actual priority: 15. | ||
| 13 | (priority-donate-chain) main should have priority 18. Actual priority: 18. | ||
| 14 | (priority-donate-chain) main should have priority 21. Actual priority: 21. | ||
| 15 | (priority-donate-chain) thread 1 got lock | ||
| 16 | (priority-donate-chain) thread 1 should have priority 21. Actual priority: 21 | ||
| 17 | (priority-donate-chain) thread 2 got lock | ||
| 18 | (priority-donate-chain) thread 2 should have priority 21. Actual priority: 21 | ||
| 19 | (priority-donate-chain) thread 3 got lock | ||
| 20 | (priority-donate-chain) thread 3 should have priority 21. Actual priority: 21 | ||
| 21 | (priority-donate-chain) thread 4 got lock | ||
| 22 | (priority-donate-chain) thread 4 should have priority 21. Actual priority: 21 | ||
| 23 | (priority-donate-chain) thread 5 got lock | ||
| 24 | (priority-donate-chain) thread 5 should have priority 21. Actual priority: 21 | ||
| 25 | (priority-donate-chain) thread 6 got lock | ||
| 26 | (priority-donate-chain) thread 6 should have priority 21. Actual priority: 21 | ||
| 27 | (priority-donate-chain) thread 7 got lock | ||
| 28 | (priority-donate-chain) thread 7 should have priority 21. Actual priority: 21 | ||
| 29 | (priority-donate-chain) thread 7 finishing with priority 21. | ||
| 30 | (priority-donate-chain) interloper 7 finished. | ||
| 31 | (priority-donate-chain) thread 6 finishing with priority 18. | ||
| 32 | (priority-donate-chain) interloper 6 finished. | ||
| 33 | (priority-donate-chain) thread 5 finishing with priority 15. | ||
| 34 | (priority-donate-chain) interloper 5 finished. | ||
| 35 | (priority-donate-chain) thread 4 finishing with priority 12. | ||
| 36 | (priority-donate-chain) interloper 4 finished. | ||
| 37 | (priority-donate-chain) thread 3 finishing with priority 9. | ||
| 38 | (priority-donate-chain) interloper 3 finished. | ||
| 39 | (priority-donate-chain) thread 2 finishing with priority 6. | ||
| 40 | (priority-donate-chain) interloper 2 finished. | ||
| 41 | (priority-donate-chain) thread 1 finishing with priority 3. | ||
| 42 | (priority-donate-chain) interloper 1 finished. | ||
| 43 | (priority-donate-chain) main finishing with priority 0. | ||
| 44 | (priority-donate-chain) end | ||
| 45 | EOF | ||
| 46 | pass; | ||
diff --git a/pintos-progos/tests/threads/priority-donate-lower.c b/pintos-progos/tests/threads/priority-donate-lower.c new file mode 100644 index 0000000..4965d75 --- /dev/null +++ b/pintos-progos/tests/threads/priority-donate-lower.c | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | /* The main thread acquires a lock. Then it creates a | ||
| 2 | higher-priority thread that blocks acquiring the lock, causing | ||
| 3 | it to donate their priorities to the main thread. The main | ||
| 4 | thread attempts to lower its priority, which should not take | ||
| 5 | effect until the donation is released. */ | ||
| 6 | |||
| 7 | #include <stdio.h> | ||
| 8 | #include "tests/threads/tests.h" | ||
| 9 | #include "threads/init.h" | ||
| 10 | #include "threads/synch.h" | ||
| 11 | #include "threads/thread.h" | ||
| 12 | |||
| 13 | static thread_func acquire_thread_func; | ||
| 14 | |||
| 15 | void | ||
| 16 | test_priority_donate_lower (void) | ||
| 17 | { | ||
| 18 | struct lock lock; | ||
| 19 | |||
| 20 | /* This test does not work with the MLFQS. */ | ||
| 21 | ASSERT (!thread_mlfqs); | ||
| 22 | |||
| 23 | /* Make sure our priority is the default. */ | ||
| 24 | ASSERT (thread_get_priority () == PRI_DEFAULT); | ||
| 25 | |||
| 26 | lock_init (&lock); | ||
| 27 | lock_acquire (&lock); | ||
| 28 | thread_create ("acquire", PRI_DEFAULT + 10, acquire_thread_func, &lock); | ||
| 29 | msg ("Main thread should have priority %d. Actual priority: %d.", | ||
| 30 | PRI_DEFAULT + 10, thread_get_priority ()); | ||
| 31 | |||
| 32 | msg ("Lowering base priority..."); | ||
| 33 | thread_set_priority (PRI_DEFAULT - 10); | ||
| 34 | msg ("Main thread should have priority %d. Actual priority: %d.", | ||
| 35 | PRI_DEFAULT + 10, thread_get_priority ()); | ||
| 36 | lock_release (&lock); | ||
| 37 | msg ("acquire must already have finished."); | ||
| 38 | msg ("Main thread should have priority %d. Actual priority: %d.", | ||
| 39 | PRI_DEFAULT - 10, thread_get_priority ()); | ||
| 40 | } | ||
| 41 | |||
| 42 | static void | ||
| 43 | acquire_thread_func (void *lock_) | ||
| 44 | { | ||
| 45 | struct lock *lock = lock_; | ||
| 46 | |||
| 47 | lock_acquire (lock); | ||
| 48 | msg ("acquire: got the lock"); | ||
| 49 | lock_release (lock); | ||
| 50 | msg ("acquire: done"); | ||
| 51 | } | ||
diff --git a/pintos-progos/tests/threads/priority-donate-lower.ck b/pintos-progos/tests/threads/priority-donate-lower.ck new file mode 100644 index 0000000..c9bb61b --- /dev/null +++ b/pintos-progos/tests/threads/priority-donate-lower.ck | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (priority-donate-lower) begin | ||
| 7 | (priority-donate-lower) Main thread should have priority 41. Actual priority: 41. | ||
| 8 | (priority-donate-lower) Lowering base priority... | ||
| 9 | (priority-donate-lower) Main thread should have priority 41. Actual priority: 41. | ||
| 10 | (priority-donate-lower) acquire: got the lock | ||
| 11 | (priority-donate-lower) acquire: done | ||
| 12 | (priority-donate-lower) acquire must already have finished. | ||
| 13 | (priority-donate-lower) Main thread should have priority 21. Actual priority: 21. | ||
| 14 | (priority-donate-lower) end | ||
| 15 | EOF | ||
| 16 | pass; | ||
diff --git a/pintos-progos/tests/threads/priority-donate-multiple.c b/pintos-progos/tests/threads/priority-donate-multiple.c new file mode 100644 index 0000000..df4689c --- /dev/null +++ b/pintos-progos/tests/threads/priority-donate-multiple.c | |||
| @@ -0,0 +1,77 @@ | |||
| 1 | /* The main thread acquires locks A and B, then it creates two | ||
| 2 | higher-priority threads. Each of these threads blocks | ||
| 3 | acquiring one of the locks and thus donate their priority to | ||
| 4 | the main thread. The main thread releases the locks in turn | ||
| 5 | and relinquishes its donated priorities. | ||
| 6 | |||
| 7 | Based on a test originally submitted for Stanford's CS 140 in | ||
| 8 | winter 1999 by Matt Franklin <startled@leland.stanford.edu>, | ||
| 9 | Greg Hutchins <gmh@leland.stanford.edu>, Yu Ping Hu | ||
| 10 | <yph@cs.stanford.edu>. Modified by arens. */ | ||
| 11 | |||
| 12 | #include <stdio.h> | ||
| 13 | #include "tests/threads/tests.h" | ||
| 14 | #include "threads/init.h" | ||
| 15 | #include "threads/synch.h" | ||
| 16 | #include "threads/thread.h" | ||
| 17 | |||
| 18 | static thread_func a_thread_func; | ||
| 19 | static thread_func b_thread_func; | ||
| 20 | |||
| 21 | void | ||
| 22 | test_priority_donate_multiple (void) | ||
| 23 | { | ||
| 24 | struct lock a, b; | ||
| 25 | |||
| 26 | /* This test does not work with the MLFQS. */ | ||
| 27 | ASSERT (!thread_mlfqs); | ||
| 28 | |||
| 29 | /* Make sure our priority is the default. */ | ||
| 30 | ASSERT (thread_get_priority () == PRI_DEFAULT); | ||
| 31 | |||
| 32 | lock_init (&a); | ||
| 33 | lock_init (&b); | ||
| 34 | |||
| 35 | lock_acquire (&a); | ||
| 36 | lock_acquire (&b); | ||
| 37 | |||
| 38 | thread_create ("a", PRI_DEFAULT + 1, a_thread_func, &a); | ||
| 39 | msg ("Main thread should have priority %d. Actual priority: %d.", | ||
| 40 | PRI_DEFAULT + 1, thread_get_priority ()); | ||
| 41 | |||
| 42 | thread_create ("b", PRI_DEFAULT + 2, b_thread_func, &b); | ||
| 43 | msg ("Main thread should have priority %d. Actual priority: %d.", | ||
| 44 | PRI_DEFAULT + 2, thread_get_priority ()); | ||
| 45 | |||
| 46 | lock_release (&b); | ||
| 47 | msg ("Thread b should have just finished."); | ||
| 48 | msg ("Main thread should have priority %d. Actual priority: %d.", | ||
| 49 | PRI_DEFAULT + 1, thread_get_priority ()); | ||
| 50 | |||
| 51 | lock_release (&a); | ||
| 52 | msg ("Thread a should have just finished."); | ||
| 53 | msg ("Main thread should have priority %d. Actual priority: %d.", | ||
| 54 | PRI_DEFAULT, thread_get_priority ()); | ||
| 55 | } | ||
| 56 | |||
| 57 | static void | ||
| 58 | a_thread_func (void *lock_) | ||
| 59 | { | ||
| 60 | struct lock *lock = lock_; | ||
| 61 | |||
| 62 | lock_acquire (lock); | ||
| 63 | msg ("Thread a acquired lock a."); | ||
| 64 | lock_release (lock); | ||
| 65 | msg ("Thread a finished."); | ||
| 66 | } | ||
| 67 | |||
| 68 | static void | ||
| 69 | b_thread_func (void *lock_) | ||
| 70 | { | ||
| 71 | struct lock *lock = lock_; | ||
| 72 | |||
| 73 | lock_acquire (lock); | ||
| 74 | msg ("Thread b acquired lock b."); | ||
| 75 | lock_release (lock); | ||
| 76 | msg ("Thread b finished."); | ||
| 77 | } | ||
diff --git a/pintos-progos/tests/threads/priority-donate-multiple.ck b/pintos-progos/tests/threads/priority-donate-multiple.ck new file mode 100644 index 0000000..0afd20b --- /dev/null +++ b/pintos-progos/tests/threads/priority-donate-multiple.ck | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (priority-donate-multiple) begin | ||
| 7 | (priority-donate-multiple) Main thread should have priority 32. Actual priority: 32. | ||
| 8 | (priority-donate-multiple) Main thread should have priority 33. Actual priority: 33. | ||
| 9 | (priority-donate-multiple) Thread b acquired lock b. | ||
| 10 | (priority-donate-multiple) Thread b finished. | ||
| 11 | (priority-donate-multiple) Thread b should have just finished. | ||
| 12 | (priority-donate-multiple) Main thread should have priority 32. Actual priority: 32. | ||
| 13 | (priority-donate-multiple) Thread a acquired lock a. | ||
| 14 | (priority-donate-multiple) Thread a finished. | ||
| 15 | (priority-donate-multiple) Thread a should have just finished. | ||
| 16 | (priority-donate-multiple) Main thread should have priority 31. Actual priority: 31. | ||
| 17 | (priority-donate-multiple) end | ||
| 18 | EOF | ||
| 19 | pass; | ||
diff --git a/pintos-progos/tests/threads/priority-donate-multiple2.c b/pintos-progos/tests/threads/priority-donate-multiple2.c new file mode 100644 index 0000000..7f65fef --- /dev/null +++ b/pintos-progos/tests/threads/priority-donate-multiple2.c | |||
| @@ -0,0 +1,90 @@ | |||
| 1 | /* The main thread acquires locks A and B, then it creates three | ||
| 2 | higher-priority threads. The first two of these threads block | ||
| 3 | acquiring one of the locks and thus donate their priority to | ||
| 4 | the main thread. The main thread releases the locks in turn | ||
| 5 | and relinquishes its donated priorities, allowing the third thread | ||
| 6 | to run. | ||
| 7 | |||
| 8 | In this test, the main thread releases the locks in a different | ||
| 9 | order compared to priority-donate-multiple.c. | ||
| 10 | |||
| 11 | Written by Godmar Back <gback@cs.vt.edu>. | ||
| 12 | Based on a test originally submitted for Stanford's CS 140 in | ||
| 13 | winter 1999 by Matt Franklin <startled@leland.stanford.edu>, | ||
| 14 | Greg Hutchins <gmh@leland.stanford.edu>, Yu Ping Hu | ||
| 15 | <yph@cs.stanford.edu>. Modified by arens. */ | ||
| 16 | |||
| 17 | #include <stdio.h> | ||
| 18 | #include "tests/threads/tests.h" | ||
| 19 | #include "threads/init.h" | ||
| 20 | #include "threads/synch.h" | ||
| 21 | #include "threads/thread.h" | ||
| 22 | |||
| 23 | static thread_func a_thread_func; | ||
| 24 | static thread_func b_thread_func; | ||
| 25 | static thread_func c_thread_func; | ||
| 26 | |||
| 27 | void | ||
| 28 | test_priority_donate_multiple2 (void) | ||
| 29 | { | ||
| 30 | struct lock a, b; | ||
| 31 | |||
| 32 | /* This test does not work with the MLFQS. */ | ||
| 33 | ASSERT (!thread_mlfqs); | ||
| 34 | |||
| 35 | /* Make sure our priority is the default. */ | ||
| 36 | ASSERT (thread_get_priority () == PRI_DEFAULT); | ||
| 37 | |||
| 38 | lock_init (&a); | ||
| 39 | lock_init (&b); | ||
| 40 | |||
| 41 | lock_acquire (&a); | ||
| 42 | lock_acquire (&b); | ||
| 43 | |||
| 44 | thread_create ("a", PRI_DEFAULT + 3, a_thread_func, &a); | ||
| 45 | msg ("Main thread should have priority %d. Actual priority: %d.", | ||
| 46 | PRI_DEFAULT + 3, thread_get_priority ()); | ||
| 47 | |||
| 48 | thread_create ("c", PRI_DEFAULT + 1, c_thread_func, NULL); | ||
| 49 | |||
| 50 | thread_create ("b", PRI_DEFAULT + 5, b_thread_func, &b); | ||
| 51 | msg ("Main thread should have priority %d. Actual priority: %d.", | ||
| 52 | PRI_DEFAULT + 5, thread_get_priority ()); | ||
| 53 | |||
| 54 | lock_release (&a); | ||
| 55 | msg ("Main thread should have priority %d. Actual priority: %d.", | ||
| 56 | PRI_DEFAULT + 5, thread_get_priority ()); | ||
| 57 | |||
| 58 | lock_release (&b); | ||
| 59 | msg ("Threads b, a, c should have just finished, in that order."); | ||
| 60 | msg ("Main thread should have priority %d. Actual priority: %d.", | ||
| 61 | PRI_DEFAULT, thread_get_priority ()); | ||
| 62 | } | ||
| 63 | |||
| 64 | static void | ||
| 65 | a_thread_func (void *lock_) | ||
| 66 | { | ||
| 67 | struct lock *lock = lock_; | ||
| 68 | |||
| 69 | lock_acquire (lock); | ||
| 70 | msg ("Thread a acquired lock a."); | ||
| 71 | lock_release (lock); | ||
| 72 | msg ("Thread a finished."); | ||
| 73 | } | ||
| 74 | |||
| 75 | static void | ||
| 76 | b_thread_func (void *lock_) | ||
| 77 | { | ||
| 78 | struct lock *lock = lock_; | ||
| 79 | |||
| 80 | lock_acquire (lock); | ||
| 81 | msg ("Thread b acquired lock b."); | ||
| 82 | lock_release (lock); | ||
| 83 | msg ("Thread b finished."); | ||
| 84 | } | ||
| 85 | |||
| 86 | static void | ||
| 87 | c_thread_func (void *a_ UNUSED) | ||
| 88 | { | ||
| 89 | msg ("Thread c finished."); | ||
| 90 | } | ||
diff --git a/pintos-progos/tests/threads/priority-donate-multiple2.ck b/pintos-progos/tests/threads/priority-donate-multiple2.ck new file mode 100644 index 0000000..b23533a --- /dev/null +++ b/pintos-progos/tests/threads/priority-donate-multiple2.ck | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (priority-donate-multiple2) begin | ||
| 7 | (priority-donate-multiple2) Main thread should have priority 34. Actual priority: 34. | ||
| 8 | (priority-donate-multiple2) Main thread should have priority 36. Actual priority: 36. | ||
| 9 | (priority-donate-multiple2) Main thread should have priority 36. Actual priority: 36. | ||
| 10 | (priority-donate-multiple2) Thread b acquired lock b. | ||
| 11 | (priority-donate-multiple2) Thread b finished. | ||
| 12 | (priority-donate-multiple2) Thread a acquired lock a. | ||
| 13 | (priority-donate-multiple2) Thread a finished. | ||
| 14 | (priority-donate-multiple2) Thread c finished. | ||
| 15 | (priority-donate-multiple2) Threads b, a, c should have just finished, in that order. | ||
| 16 | (priority-donate-multiple2) Main thread should have priority 31. Actual priority: 31. | ||
| 17 | (priority-donate-multiple2) end | ||
| 18 | EOF | ||
| 19 | pass; | ||
diff --git a/pintos-progos/tests/threads/priority-donate-nest.c b/pintos-progos/tests/threads/priority-donate-nest.c new file mode 100644 index 0000000..3a3a9a5 --- /dev/null +++ b/pintos-progos/tests/threads/priority-donate-nest.c | |||
| @@ -0,0 +1,94 @@ | |||
| 1 | /* Low-priority main thread L acquires lock A. Medium-priority | ||
| 2 | thread M then acquires lock B then blocks on acquiring lock A. | ||
| 3 | High-priority thread H then blocks on acquiring lock B. Thus, | ||
| 4 | thread H donates its priority to M, which in turn donates it | ||
| 5 | to thread L. | ||
| 6 | |||
| 7 | Based on a test originally submitted for Stanford's CS 140 in | ||
| 8 | winter 1999 by Matt Franklin <startled@leland.stanford.edu>, | ||
| 9 | Greg Hutchins <gmh@leland.stanford.edu>, Yu Ping Hu | ||
| 10 | <yph@cs.stanford.edu>. Modified by arens. */ | ||
| 11 | |||
| 12 | #include <stdio.h> | ||
| 13 | #include "tests/threads/tests.h" | ||
| 14 | #include "threads/init.h" | ||
| 15 | #include "threads/synch.h" | ||
| 16 | #include "threads/thread.h" | ||
| 17 | |||
| 18 | struct locks | ||
| 19 | { | ||
| 20 | struct lock *a; | ||
| 21 | struct lock *b; | ||
| 22 | }; | ||
| 23 | |||
| 24 | static thread_func medium_thread_func; | ||
| 25 | static thread_func high_thread_func; | ||
| 26 | |||
| 27 | void | ||
| 28 | test_priority_donate_nest (void) | ||
| 29 | { | ||
| 30 | struct lock a, b; | ||
| 31 | struct locks locks; | ||
| 32 | |||
| 33 | /* This test does not work with the MLFQS. */ | ||
| 34 | ASSERT (!thread_mlfqs); | ||
| 35 | |||
| 36 | /* Make sure our priority is the default. */ | ||
| 37 | ASSERT (thread_get_priority () == PRI_DEFAULT); | ||
| 38 | |||
| 39 | lock_init (&a); | ||
| 40 | lock_init (&b); | ||
| 41 | |||
| 42 | lock_acquire (&a); | ||
| 43 | |||
| 44 | locks.a = &a; | ||
| 45 | locks.b = &b; | ||
| 46 | thread_create ("medium", PRI_DEFAULT + 1, medium_thread_func, &locks); | ||
| 47 | thread_yield (); | ||
| 48 | msg ("Low thread should have priority %d. Actual priority: %d.", | ||
| 49 | PRI_DEFAULT + 1, thread_get_priority ()); | ||
| 50 | |||
| 51 | thread_create ("high", PRI_DEFAULT + 2, high_thread_func, &b); | ||
| 52 | thread_yield (); | ||
| 53 | msg ("Low thread should have priority %d. Actual priority: %d.", | ||
| 54 | PRI_DEFAULT + 2, thread_get_priority ()); | ||
| 55 | |||
| 56 | lock_release (&a); | ||
| 57 | thread_yield (); | ||
| 58 | msg ("Medium thread should just have finished."); | ||
| 59 | msg ("Low thread should have priority %d. Actual priority: %d.", | ||
| 60 | PRI_DEFAULT, thread_get_priority ()); | ||
| 61 | } | ||
| 62 | |||
| 63 | static void | ||
| 64 | medium_thread_func (void *locks_) | ||
| 65 | { | ||
| 66 | struct locks *locks = locks_; | ||
| 67 | |||
| 68 | lock_acquire (locks->b); | ||
| 69 | lock_acquire (locks->a); | ||
| 70 | |||
| 71 | msg ("Medium thread should have priority %d. Actual priority: %d.", | ||
| 72 | PRI_DEFAULT + 2, thread_get_priority ()); | ||
| 73 | msg ("Medium thread got the lock."); | ||
| 74 | |||
| 75 | lock_release (locks->a); | ||
| 76 | thread_yield (); | ||
| 77 | |||
| 78 | lock_release (locks->b); | ||
| 79 | thread_yield (); | ||
| 80 | |||
| 81 | msg ("High thread should have just finished."); | ||
| 82 | msg ("Middle thread finished."); | ||
| 83 | } | ||
| 84 | |||
| 85 | static void | ||
| 86 | high_thread_func (void *lock_) | ||
| 87 | { | ||
| 88 | struct lock *lock = lock_; | ||
| 89 | |||
| 90 | lock_acquire (lock); | ||
| 91 | msg ("High thread got the lock."); | ||
| 92 | lock_release (lock); | ||
| 93 | msg ("High thread finished."); | ||
| 94 | } | ||
diff --git a/pintos-progos/tests/threads/priority-donate-nest.ck b/pintos-progos/tests/threads/priority-donate-nest.ck new file mode 100644 index 0000000..923460e --- /dev/null +++ b/pintos-progos/tests/threads/priority-donate-nest.ck | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (priority-donate-nest) begin | ||
| 7 | (priority-donate-nest) Low thread should have priority 32. Actual priority: 32. | ||
| 8 | (priority-donate-nest) Low thread should have priority 33. Actual priority: 33. | ||
| 9 | (priority-donate-nest) Medium thread should have priority 33. Actual priority: 33. | ||
| 10 | (priority-donate-nest) Medium thread got the lock. | ||
| 11 | (priority-donate-nest) High thread got the lock. | ||
| 12 | (priority-donate-nest) High thread finished. | ||
| 13 | (priority-donate-nest) High thread should have just finished. | ||
| 14 | (priority-donate-nest) Middle thread finished. | ||
| 15 | (priority-donate-nest) Medium thread should just have finished. | ||
| 16 | (priority-donate-nest) Low thread should have priority 31. Actual priority: 31. | ||
| 17 | (priority-donate-nest) end | ||
| 18 | EOF | ||
| 19 | pass; | ||
diff --git a/pintos-progos/tests/threads/priority-donate-one.c b/pintos-progos/tests/threads/priority-donate-one.c new file mode 100644 index 0000000..3189f3a --- /dev/null +++ b/pintos-progos/tests/threads/priority-donate-one.c | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | /* The main thread acquires a lock. Then it creates two | ||
| 2 | higher-priority threads that block acquiring the lock, causing | ||
| 3 | them to donate their priorities to the main thread. When the | ||
| 4 | main thread releases the lock, the other threads should | ||
| 5 | acquire it in priority order. | ||
| 6 | |||
| 7 | Based on a test originally submitted for Stanford's CS 140 in | ||
| 8 | winter 1999 by Matt Franklin <startled@leland.stanford.edu>, | ||
| 9 | Greg Hutchins <gmh@leland.stanford.edu>, Yu Ping Hu | ||
| 10 | <yph@cs.stanford.edu>. Modified by arens. */ | ||
| 11 | |||
| 12 | #include <stdio.h> | ||
| 13 | #include "tests/threads/tests.h" | ||
| 14 | #include "threads/init.h" | ||
| 15 | #include "threads/synch.h" | ||
| 16 | #include "threads/thread.h" | ||
| 17 | |||
| 18 | static thread_func acquire1_thread_func; | ||
| 19 | static thread_func acquire2_thread_func; | ||
| 20 | |||
| 21 | void | ||
| 22 | test_priority_donate_one (void) | ||
| 23 | { | ||
| 24 | struct lock lock; | ||
| 25 | |||
| 26 | /* This test does not work with the MLFQS. */ | ||
| 27 | ASSERT (!thread_mlfqs); | ||
| 28 | |||
| 29 | /* Make sure our priority is the default. */ | ||
| 30 | ASSERT (thread_get_priority () == PRI_DEFAULT); | ||
| 31 | |||
| 32 | lock_init (&lock); | ||
| 33 | lock_acquire (&lock); | ||
| 34 | thread_create ("acquire1", PRI_DEFAULT + 1, acquire1_thread_func, &lock); | ||
| 35 | msg ("This thread should have priority %d. Actual priority: %d.", | ||
| 36 | PRI_DEFAULT + 1, thread_get_priority ()); | ||
| 37 | thread_create ("acquire2", PRI_DEFAULT + 2, acquire2_thread_func, &lock); | ||
| 38 | msg ("This thread should have priority %d. Actual priority: %d.", | ||
| 39 | PRI_DEFAULT + 2, thread_get_priority ()); | ||
| 40 | lock_release (&lock); | ||
| 41 | msg ("acquire2, acquire1 must already have finished, in that order."); | ||
| 42 | msg ("This should be the last line before finishing this test."); | ||
| 43 | } | ||
| 44 | |||
| 45 | static void | ||
| 46 | acquire1_thread_func (void *lock_) | ||
| 47 | { | ||
| 48 | struct lock *lock = lock_; | ||
| 49 | |||
| 50 | lock_acquire (lock); | ||
| 51 | msg ("acquire1: got the lock"); | ||
| 52 | lock_release (lock); | ||
| 53 | msg ("acquire1: done"); | ||
| 54 | } | ||
| 55 | |||
| 56 | static void | ||
| 57 | acquire2_thread_func (void *lock_) | ||
| 58 | { | ||
| 59 | struct lock *lock = lock_; | ||
| 60 | |||
| 61 | lock_acquire (lock); | ||
| 62 | msg ("acquire2: got the lock"); | ||
| 63 | lock_release (lock); | ||
| 64 | msg ("acquire2: done"); | ||
| 65 | } | ||
diff --git a/pintos-progos/tests/threads/priority-donate-one.ck b/pintos-progos/tests/threads/priority-donate-one.ck new file mode 100644 index 0000000..b7c8e6f --- /dev/null +++ b/pintos-progos/tests/threads/priority-donate-one.ck | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (priority-donate-one) begin | ||
| 7 | (priority-donate-one) This thread should have priority 32. Actual priority: 32. | ||
| 8 | (priority-donate-one) This thread should have priority 33. Actual priority: 33. | ||
| 9 | (priority-donate-one) acquire2: got the lock | ||
| 10 | (priority-donate-one) acquire2: done | ||
| 11 | (priority-donate-one) acquire1: got the lock | ||
| 12 | (priority-donate-one) acquire1: done | ||
| 13 | (priority-donate-one) acquire2, acquire1 must already have finished, in that order. | ||
| 14 | (priority-donate-one) This should be the last line before finishing this test. | ||
| 15 | (priority-donate-one) end | ||
| 16 | EOF | ||
| 17 | pass; | ||
diff --git a/pintos-progos/tests/threads/priority-donate-sema.c b/pintos-progos/tests/threads/priority-donate-sema.c new file mode 100644 index 0000000..b33cb72 --- /dev/null +++ b/pintos-progos/tests/threads/priority-donate-sema.c | |||
| @@ -0,0 +1,82 @@ | |||
| 1 | /* Low priority thread L acquires a lock, then blocks downing a | ||
| 2 | semaphore. Medium priority thread M then blocks waiting on | ||
| 3 | the same semaphore. Next, high priority thread H attempts to | ||
| 4 | acquire the lock, donating its priority to L. | ||
| 5 | |||
| 6 | Next, the main thread ups the semaphore, waking up L. L | ||
| 7 | releases the lock, which wakes up H. H "up"s the semaphore, | ||
| 8 | waking up M. H terminates, then M, then L, and finally the | ||
| 9 | main thread. | ||
| 10 | |||
| 11 | Written by Godmar Back <gback@cs.vt.edu>. */ | ||
| 12 | |||
| 13 | #include <stdio.h> | ||
| 14 | #include "tests/threads/tests.h" | ||
| 15 | #include "threads/init.h" | ||
| 16 | #include "threads/synch.h" | ||
| 17 | #include "threads/thread.h" | ||
| 18 | |||
| 19 | struct lock_and_sema | ||
| 20 | { | ||
| 21 | struct lock lock; | ||
| 22 | struct semaphore sema; | ||
| 23 | }; | ||
| 24 | |||
| 25 | static thread_func l_thread_func; | ||
| 26 | static thread_func m_thread_func; | ||
| 27 | static thread_func h_thread_func; | ||
| 28 | |||
| 29 | void | ||
| 30 | test_priority_donate_sema (void) | ||
| 31 | { | ||
| 32 | struct lock_and_sema ls; | ||
| 33 | |||
| 34 | /* This test does not work with the MLFQS. */ | ||
| 35 | ASSERT (!thread_mlfqs); | ||
| 36 | |||
| 37 | /* Make sure our priority is the default. */ | ||
| 38 | ASSERT (thread_get_priority () == PRI_DEFAULT); | ||
| 39 | |||
| 40 | lock_init (&ls.lock); | ||
| 41 | sema_init (&ls.sema, 0); | ||
| 42 | thread_create ("low", PRI_DEFAULT + 1, l_thread_func, &ls); | ||
| 43 | thread_create ("med", PRI_DEFAULT + 3, m_thread_func, &ls); | ||
| 44 | thread_create ("high", PRI_DEFAULT + 5, h_thread_func, &ls); | ||
| 45 | sema_up (&ls.sema); | ||
| 46 | msg ("Main thread finished."); | ||
| 47 | } | ||
| 48 | |||
| 49 | static void | ||
| 50 | l_thread_func (void *ls_) | ||
| 51 | { | ||
| 52 | struct lock_and_sema *ls = ls_; | ||
| 53 | |||
| 54 | lock_acquire (&ls->lock); | ||
| 55 | msg ("Thread L acquired lock."); | ||
| 56 | sema_down (&ls->sema); | ||
| 57 | msg ("Thread L downed semaphore."); | ||
| 58 | lock_release (&ls->lock); | ||
| 59 | msg ("Thread L finished."); | ||
| 60 | } | ||
| 61 | |||
| 62 | static void | ||
| 63 | m_thread_func (void *ls_) | ||
| 64 | { | ||
| 65 | struct lock_and_sema *ls = ls_; | ||
| 66 | |||
| 67 | sema_down (&ls->sema); | ||
| 68 | msg ("Thread M finished."); | ||
| 69 | } | ||
| 70 | |||
| 71 | static void | ||
| 72 | h_thread_func (void *ls_) | ||
| 73 | { | ||
| 74 | struct lock_and_sema *ls = ls_; | ||
| 75 | |||
| 76 | lock_acquire (&ls->lock); | ||
| 77 | msg ("Thread H acquired lock."); | ||
| 78 | |||
| 79 | sema_up (&ls->sema); | ||
| 80 | lock_release (&ls->lock); | ||
| 81 | msg ("Thread H finished."); | ||
| 82 | } | ||
diff --git a/pintos-progos/tests/threads/priority-donate-sema.ck b/pintos-progos/tests/threads/priority-donate-sema.ck new file mode 100644 index 0000000..92b8d07 --- /dev/null +++ b/pintos-progos/tests/threads/priority-donate-sema.ck | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (priority-donate-sema) begin | ||
| 7 | (priority-donate-sema) Thread L acquired lock. | ||
| 8 | (priority-donate-sema) Thread L downed semaphore. | ||
| 9 | (priority-donate-sema) Thread H acquired lock. | ||
| 10 | (priority-donate-sema) Thread H finished. | ||
| 11 | (priority-donate-sema) Thread M finished. | ||
| 12 | (priority-donate-sema) Thread L finished. | ||
| 13 | (priority-donate-sema) Main thread finished. | ||
| 14 | (priority-donate-sema) end | ||
| 15 | EOF | ||
| 16 | pass; | ||
diff --git a/pintos-progos/tests/threads/priority-fifo.c b/pintos-progos/tests/threads/priority-fifo.c new file mode 100644 index 0000000..3af98a3 --- /dev/null +++ b/pintos-progos/tests/threads/priority-fifo.c | |||
| @@ -0,0 +1,99 @@ | |||
| 1 | /* Creates several threads all at the same priority and ensures | ||
| 2 | that they consistently run in the same round-robin order. | ||
| 3 | |||
| 4 | Based on a test originally submitted for Stanford's CS 140 in | ||
| 5 | winter 1999 by by Matt Franklin | ||
| 6 | <startled@leland.stanford.edu>, Greg Hutchins | ||
| 7 | <gmh@leland.stanford.edu>, Yu Ping Hu <yph@cs.stanford.edu>. | ||
| 8 | Modified by arens. */ | ||
| 9 | |||
| 10 | #include <stdio.h> | ||
| 11 | #include "tests/threads/tests.h" | ||
| 12 | #include "threads/init.h" | ||
| 13 | #include "devices/timer.h" | ||
| 14 | #include "threads/malloc.h" | ||
| 15 | #include "threads/synch.h" | ||
| 16 | #include "threads/thread.h" | ||
| 17 | |||
| 18 | struct simple_thread_data | ||
| 19 | { | ||
| 20 | int id; /* Sleeper ID. */ | ||
| 21 | int iterations; /* Iterations so far. */ | ||
| 22 | struct lock *lock; /* Lock on output. */ | ||
| 23 | int **op; /* Output buffer position. */ | ||
| 24 | }; | ||
| 25 | |||
| 26 | #define THREAD_CNT 16 | ||
| 27 | #define ITER_CNT 16 | ||
| 28 | |||
| 29 | static thread_func simple_thread_func; | ||
| 30 | |||
| 31 | void | ||
| 32 | test_priority_fifo (void) | ||
| 33 | { | ||
| 34 | struct simple_thread_data data[THREAD_CNT]; | ||
| 35 | struct lock lock; | ||
| 36 | int *output, *op; | ||
| 37 | int i, cnt; | ||
| 38 | |||
| 39 | /* This test does not work with the MLFQS. */ | ||
| 40 | ASSERT (!thread_mlfqs); | ||
| 41 | |||
| 42 | /* Make sure our priority is the default. */ | ||
| 43 | ASSERT (thread_get_priority () == PRI_DEFAULT); | ||
| 44 | |||
| 45 | msg ("%d threads will iterate %d times in the same order each time.", | ||
| 46 | THREAD_CNT, ITER_CNT); | ||
| 47 | msg ("If the order varies then there is a bug."); | ||
| 48 | |||
| 49 | output = op = malloc (sizeof *output * THREAD_CNT * ITER_CNT * 2); | ||
| 50 | ASSERT (output != NULL); | ||
| 51 | lock_init (&lock); | ||
| 52 | |||
| 53 | thread_set_priority (PRI_DEFAULT + 2); | ||
| 54 | for (i = 0; i < THREAD_CNT; i++) | ||
| 55 | { | ||
| 56 | char name[16]; | ||
| 57 | struct simple_thread_data *d = data + i; | ||
| 58 | snprintf (name, sizeof name, "%d", i); | ||
| 59 | d->id = i; | ||
| 60 | d->iterations = 0; | ||
| 61 | d->lock = &lock; | ||
| 62 | d->op = &op; | ||
| 63 | thread_create (name, PRI_DEFAULT + 1, simple_thread_func, d); | ||
| 64 | } | ||
| 65 | |||
| 66 | thread_set_priority (PRI_DEFAULT); | ||
| 67 | /* All the other threads now run to termination here. */ | ||
| 68 | ASSERT (lock.holder == NULL); | ||
| 69 | |||
| 70 | cnt = 0; | ||
| 71 | for (; output < op; output++) | ||
| 72 | { | ||
| 73 | struct simple_thread_data *d; | ||
| 74 | |||
| 75 | ASSERT (*output >= 0 && *output < THREAD_CNT); | ||
| 76 | d = data + *output; | ||
| 77 | if (cnt % THREAD_CNT == 0) | ||
| 78 | printf ("(priority-fifo) iteration:"); | ||
| 79 | printf (" %d", d->id); | ||
| 80 | if (++cnt % THREAD_CNT == 0) | ||
| 81 | printf ("\n"); | ||
| 82 | d->iterations++; | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | static void | ||
| 87 | simple_thread_func (void *data_) | ||
| 88 | { | ||
| 89 | struct simple_thread_data *data = data_; | ||
| 90 | int i; | ||
| 91 | |||
| 92 | for (i = 0; i < ITER_CNT; i++) | ||
| 93 | { | ||
| 94 | lock_acquire (data->lock); | ||
| 95 | *(*data->op)++ = data->id; | ||
| 96 | lock_release (data->lock); | ||
| 97 | thread_yield (); | ||
| 98 | } | ||
| 99 | } | ||
diff --git a/pintos-progos/tests/threads/priority-fifo.ck b/pintos-progos/tests/threads/priority-fifo.ck new file mode 100644 index 0000000..11f1dd3 --- /dev/null +++ b/pintos-progos/tests/threads/priority-fifo.ck | |||
| @@ -0,0 +1,63 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | |||
| 3 | # The expected output looks like this: | ||
| 4 | # | ||
| 5 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 6 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 7 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 8 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 9 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 10 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 11 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 12 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 13 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 14 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 15 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 16 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 17 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 18 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 19 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 20 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 21 | # | ||
| 22 | # A different permutation of 0...15 is acceptable, but every line must | ||
| 23 | # be in the same order. | ||
| 24 | |||
| 25 | use strict; | ||
| 26 | use warnings; | ||
| 27 | use tests::tests; | ||
| 28 | |||
| 29 | our ($test); | ||
| 30 | my (@output) = read_text_file ("$test.output"); | ||
| 31 | |||
| 32 | common_checks ("run", @output); | ||
| 33 | |||
| 34 | my ($thread_cnt) = 16; | ||
| 35 | my ($iter_cnt) = 16; | ||
| 36 | my (@order); | ||
| 37 | my (@t) = (-1) x $thread_cnt; | ||
| 38 | |||
| 39 | my (@iterations) = grep (/iteration:/, @output); | ||
| 40 | fail "No iterations found in output.\n" if !@iterations; | ||
| 41 | |||
| 42 | my (@numbering) = $iterations[0] =~ /(\d+)/g; | ||
| 43 | fail "First iteration does not list exactly $thread_cnt threads.\n" | ||
| 44 | if @numbering != $thread_cnt; | ||
| 45 | |||
| 46 | my (@sorted_numbering) = sort { $a <=> $b } @numbering; | ||
| 47 | for my $i (0...$#sorted_numbering) { | ||
| 48 | if ($sorted_numbering[$i] != $i) { | ||
| 49 | fail "First iteration does not list all threads " | ||
| 50 | . "0...$#sorted_numbering\n"; | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | for my $i (1...$#iterations) { | ||
| 55 | if ($iterations[$i] ne $iterations[0]) { | ||
| 56 | fail "Iteration $i differs from iteration 0\n"; | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | fail "$iter_cnt iterations expected but " . scalar (@iterations) . " found\n" | ||
| 61 | if $iter_cnt != @iterations; | ||
| 62 | |||
| 63 | pass; | ||
diff --git a/pintos-progos/tests/threads/priority-preempt.c b/pintos-progos/tests/threads/priority-preempt.c new file mode 100644 index 0000000..3c3aacb --- /dev/null +++ b/pintos-progos/tests/threads/priority-preempt.c | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | /* Ensures that a high-priority thread really preempts. | ||
| 2 | |||
| 3 | Based on a test originally submitted for Stanford's CS 140 in | ||
| 4 | winter 1999 by by Matt Franklin | ||
| 5 | <startled@leland.stanford.edu>, Greg Hutchins | ||
| 6 | <gmh@leland.stanford.edu>, Yu Ping Hu <yph@cs.stanford.edu>. | ||
| 7 | Modified by arens. */ | ||
| 8 | |||
| 9 | #include <stdio.h> | ||
| 10 | #include "tests/threads/tests.h" | ||
| 11 | #include "threads/init.h" | ||
| 12 | #include "threads/synch.h" | ||
| 13 | #include "threads/thread.h" | ||
| 14 | |||
| 15 | static thread_func simple_thread_func; | ||
| 16 | |||
| 17 | void | ||
| 18 | test_priority_preempt (void) | ||
| 19 | { | ||
| 20 | /* This test does not work with the MLFQS. */ | ||
| 21 | ASSERT (!thread_mlfqs); | ||
| 22 | |||
| 23 | /* Make sure our priority is the default. */ | ||
| 24 | ASSERT (thread_get_priority () == PRI_DEFAULT); | ||
| 25 | |||
| 26 | thread_create ("high-priority", PRI_DEFAULT + 1, simple_thread_func, NULL); | ||
| 27 | msg ("The high-priority thread should have already completed."); | ||
| 28 | } | ||
| 29 | |||
| 30 | static void | ||
| 31 | simple_thread_func (void *aux UNUSED) | ||
| 32 | { | ||
| 33 | int i; | ||
| 34 | |||
| 35 | for (i = 0; i < 5; i++) | ||
| 36 | { | ||
| 37 | msg ("Thread %s iteration %d", thread_name (), i); | ||
| 38 | thread_yield (); | ||
| 39 | } | ||
| 40 | msg ("Thread %s done!", thread_name ()); | ||
| 41 | } | ||
diff --git a/pintos-progos/tests/threads/priority-preempt.ck b/pintos-progos/tests/threads/priority-preempt.ck new file mode 100644 index 0000000..43a26ee --- /dev/null +++ b/pintos-progos/tests/threads/priority-preempt.ck | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (priority-preempt) begin | ||
| 7 | (priority-preempt) Thread high-priority iteration 0 | ||
| 8 | (priority-preempt) Thread high-priority iteration 1 | ||
| 9 | (priority-preempt) Thread high-priority iteration 2 | ||
| 10 | (priority-preempt) Thread high-priority iteration 3 | ||
| 11 | (priority-preempt) Thread high-priority iteration 4 | ||
| 12 | (priority-preempt) Thread high-priority done! | ||
| 13 | (priority-preempt) The high-priority thread should have already completed. | ||
| 14 | (priority-preempt) end | ||
| 15 | EOF | ||
| 16 | pass; | ||
diff --git a/pintos-progos/tests/threads/priority-sema.c b/pintos-progos/tests/threads/priority-sema.c new file mode 100644 index 0000000..2834a88 --- /dev/null +++ b/pintos-progos/tests/threads/priority-sema.c | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | /* Tests that the highest-priority thread waiting on a semaphore | ||
| 2 | is the first to wake up. */ | ||
| 3 | |||
| 4 | #include <stdio.h> | ||
| 5 | #include "tests/threads/tests.h" | ||
| 6 | #include "threads/init.h" | ||
| 7 | #include "threads/malloc.h" | ||
| 8 | #include "threads/synch.h" | ||
| 9 | #include "threads/thread.h" | ||
| 10 | #include "devices/timer.h" | ||
| 11 | |||
| 12 | static thread_func priority_sema_thread; | ||
| 13 | static struct semaphore sema; | ||
| 14 | |||
| 15 | void | ||
| 16 | test_priority_sema (void) | ||
| 17 | { | ||
| 18 | int i; | ||
| 19 | |||
| 20 | /* This test does not work with the MLFQS. */ | ||
| 21 | ASSERT (!thread_mlfqs); | ||
| 22 | |||
| 23 | sema_init (&sema, 0); | ||
| 24 | thread_set_priority (PRI_MIN); | ||
| 25 | for (i = 0; i < 10; i++) | ||
| 26 | { | ||
| 27 | int priority = PRI_DEFAULT - (i + 3) % 10 - 1; | ||
| 28 | char name[16]; | ||
| 29 | snprintf (name, sizeof name, "priority %d", priority); | ||
| 30 | thread_create (name, priority, priority_sema_thread, NULL); | ||
| 31 | } | ||
| 32 | |||
| 33 | for (i = 0; i < 10; i++) | ||
| 34 | { | ||
| 35 | sema_up (&sema); | ||
| 36 | msg ("Back in main thread."); | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | static void | ||
| 41 | priority_sema_thread (void *aux UNUSED) | ||
| 42 | { | ||
| 43 | sema_down (&sema); | ||
| 44 | msg ("Thread %s woke up.", thread_name ()); | ||
| 45 | } | ||
diff --git a/pintos-progos/tests/threads/priority-sema.ck b/pintos-progos/tests/threads/priority-sema.ck new file mode 100644 index 0000000..559988d --- /dev/null +++ b/pintos-progos/tests/threads/priority-sema.ck | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (priority-sema) begin | ||
| 7 | (priority-sema) Thread priority 30 woke up. | ||
| 8 | (priority-sema) Back in main thread. | ||
| 9 | (priority-sema) Thread priority 29 woke up. | ||
| 10 | (priority-sema) Back in main thread. | ||
| 11 | (priority-sema) Thread priority 28 woke up. | ||
| 12 | (priority-sema) Back in main thread. | ||
| 13 | (priority-sema) Thread priority 27 woke up. | ||
| 14 | (priority-sema) Back in main thread. | ||
| 15 | (priority-sema) Thread priority 26 woke up. | ||
| 16 | (priority-sema) Back in main thread. | ||
| 17 | (priority-sema) Thread priority 25 woke up. | ||
| 18 | (priority-sema) Back in main thread. | ||
| 19 | (priority-sema) Thread priority 24 woke up. | ||
| 20 | (priority-sema) Back in main thread. | ||
| 21 | (priority-sema) Thread priority 23 woke up. | ||
| 22 | (priority-sema) Back in main thread. | ||
| 23 | (priority-sema) Thread priority 22 woke up. | ||
| 24 | (priority-sema) Back in main thread. | ||
| 25 | (priority-sema) Thread priority 21 woke up. | ||
| 26 | (priority-sema) Back in main thread. | ||
| 27 | (priority-sema) end | ||
| 28 | EOF | ||
| 29 | pass; | ||
diff --git a/pintos-progos/tests/threads/tests.c b/pintos-progos/tests/threads/tests.c new file mode 100644 index 0000000..af15aee --- /dev/null +++ b/pintos-progos/tests/threads/tests.c | |||
| @@ -0,0 +1,102 @@ | |||
| 1 | #include "tests/threads/tests.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include <string.h> | ||
| 4 | #include <stdio.h> | ||
| 5 | |||
| 6 | struct test | ||
| 7 | { | ||
| 8 | const char *name; | ||
| 9 | test_func *function; | ||
| 10 | }; | ||
| 11 | |||
| 12 | static const struct test tests[] = | ||
| 13 | { | ||
| 14 | {"alarm-single", test_alarm_single}, | ||
| 15 | {"alarm-multiple", test_alarm_multiple}, | ||
| 16 | {"alarm-simultaneous", test_alarm_simultaneous}, | ||
| 17 | {"alarm-priority", test_alarm_priority}, | ||
| 18 | {"alarm-zero", test_alarm_zero}, | ||
| 19 | {"alarm-negative", test_alarm_negative}, | ||
| 20 | {"priority-change", test_priority_change}, | ||
| 21 | {"priority-donate-one", test_priority_donate_one}, | ||
| 22 | {"priority-donate-multiple", test_priority_donate_multiple}, | ||
| 23 | {"priority-donate-multiple2", test_priority_donate_multiple2}, | ||
| 24 | {"priority-donate-nest", test_priority_donate_nest}, | ||
| 25 | {"priority-donate-sema", test_priority_donate_sema}, | ||
| 26 | {"priority-donate-lower", test_priority_donate_lower}, | ||
| 27 | {"priority-donate-chain", test_priority_donate_chain}, | ||
| 28 | {"priority-fifo", test_priority_fifo}, | ||
| 29 | {"priority-preempt", test_priority_preempt}, | ||
| 30 | {"priority-sema", test_priority_sema}, | ||
| 31 | {"priority-condvar", test_priority_condvar}, | ||
| 32 | {"mlfqs-load-1", test_mlfqs_load_1}, | ||
| 33 | {"mlfqs-load-60", test_mlfqs_load_60}, | ||
| 34 | {"mlfqs-load-avg", test_mlfqs_load_avg}, | ||
| 35 | {"mlfqs-recent-1", test_mlfqs_recent_1}, | ||
| 36 | {"mlfqs-fair-2", test_mlfqs_fair_2}, | ||
| 37 | {"mlfqs-fair-20", test_mlfqs_fair_20}, | ||
| 38 | {"mlfqs-nice-2", test_mlfqs_nice_2}, | ||
| 39 | {"mlfqs-nice-10", test_mlfqs_nice_10}, | ||
| 40 | {"mlfqs-block", test_mlfqs_block}, | ||
| 41 | }; | ||
| 42 | |||
| 43 | static const char *test_name; | ||
| 44 | |||
| 45 | /* Runs the test named NAME. */ | ||
| 46 | void | ||
| 47 | run_test (const char *name) | ||
| 48 | { | ||
| 49 | const struct test *t; | ||
| 50 | |||
| 51 | for (t = tests; t < tests + sizeof tests / sizeof *tests; t++) | ||
| 52 | if (!strcmp (name, t->name)) | ||
| 53 | { | ||
| 54 | test_name = name; | ||
| 55 | msg ("begin"); | ||
| 56 | t->function (); | ||
| 57 | msg ("end"); | ||
| 58 | return; | ||
| 59 | } | ||
| 60 | PANIC ("no test named \"%s\"", name); | ||
| 61 | } | ||
| 62 | |||
| 63 | /* Prints FORMAT as if with printf(), | ||
| 64 | prefixing the output by the name of the test | ||
| 65 | and following it with a new-line character. */ | ||
| 66 | void | ||
| 67 | msg (const char *format, ...) | ||
| 68 | { | ||
| 69 | va_list args; | ||
| 70 | |||
| 71 | printf ("(%s) ", test_name); | ||
| 72 | va_start (args, format); | ||
| 73 | vprintf (format, args); | ||
| 74 | va_end (args); | ||
| 75 | putchar ('\n'); | ||
| 76 | } | ||
| 77 | |||
| 78 | /* Prints failure message FORMAT as if with printf(), | ||
| 79 | prefixing the output by the name of the test and FAIL: | ||
| 80 | and following it with a new-line character, | ||
| 81 | and then panics the kernel. */ | ||
| 82 | void | ||
| 83 | fail (const char *format, ...) | ||
| 84 | { | ||
| 85 | va_list args; | ||
| 86 | |||
| 87 | printf ("(%s) FAIL: ", test_name); | ||
| 88 | va_start (args, format); | ||
| 89 | vprintf (format, args); | ||
| 90 | va_end (args); | ||
| 91 | putchar ('\n'); | ||
| 92 | |||
| 93 | PANIC ("test failed"); | ||
| 94 | } | ||
| 95 | |||
| 96 | /* Prints a message indicating the current test passed. */ | ||
| 97 | void | ||
| 98 | pass (void) | ||
| 99 | { | ||
| 100 | printf ("(%s) PASS\n", test_name); | ||
| 101 | } | ||
| 102 | |||
diff --git a/pintos-progos/tests/threads/tests.h b/pintos-progos/tests/threads/tests.h new file mode 100644 index 0000000..cd9d489 --- /dev/null +++ b/pintos-progos/tests/threads/tests.h | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | #ifndef TESTS_THREADS_TESTS_H | ||
| 2 | #define TESTS_THREADS_TESTS_H | ||
| 3 | |||
| 4 | void run_test (const char *); | ||
| 5 | |||
| 6 | typedef void test_func (void); | ||
| 7 | |||
| 8 | extern test_func test_alarm_single; | ||
| 9 | extern test_func test_alarm_multiple; | ||
| 10 | extern test_func test_alarm_simultaneous; | ||
| 11 | extern test_func test_alarm_priority; | ||
| 12 | extern test_func test_alarm_zero; | ||
| 13 | extern test_func test_alarm_negative; | ||
| 14 | extern test_func test_priority_change; | ||
| 15 | extern test_func test_priority_donate_one; | ||
| 16 | extern test_func test_priority_donate_multiple; | ||
| 17 | extern test_func test_priority_donate_multiple2; | ||
| 18 | extern test_func test_priority_donate_sema; | ||
| 19 | extern test_func test_priority_donate_nest; | ||
| 20 | extern test_func test_priority_donate_lower; | ||
| 21 | extern test_func test_priority_donate_chain; | ||
| 22 | extern test_func test_priority_fifo; | ||
| 23 | extern test_func test_priority_preempt; | ||
| 24 | extern test_func test_priority_sema; | ||
| 25 | extern test_func test_priority_condvar; | ||
| 26 | extern test_func test_mlfqs_load_1; | ||
| 27 | extern test_func test_mlfqs_load_60; | ||
| 28 | extern test_func test_mlfqs_load_avg; | ||
| 29 | extern test_func test_mlfqs_recent_1; | ||
| 30 | extern test_func test_mlfqs_fair_2; | ||
| 31 | extern test_func test_mlfqs_fair_20; | ||
| 32 | extern test_func test_mlfqs_nice_2; | ||
| 33 | extern test_func test_mlfqs_nice_10; | ||
| 34 | extern test_func test_mlfqs_block; | ||
| 35 | |||
| 36 | void msg (const char *, ...); | ||
| 37 | void fail (const char *, ...); | ||
| 38 | void pass (void); | ||
| 39 | |||
| 40 | #endif /* tests/threads/tests.h */ | ||
| 41 | |||
diff --git a/pintos-progos/tests/userprog/Make.tests b/pintos-progos/tests/userprog/Make.tests new file mode 100644 index 0000000..88b0737 --- /dev/null +++ b/pintos-progos/tests/userprog/Make.tests | |||
| @@ -0,0 +1,133 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | |||
| 3 | tests/%.output: FILESYSSOURCE = --filesys-size=2 | ||
| 4 | tests/%.output: PUTFILES = $(filter-out kernel.bin loader.bin, $^) | ||
| 5 | |||
| 6 | tests/userprog_TESTS = $(addprefix tests/userprog/,args-none \ | ||
| 7 | args-single args-multiple args-many args-dbl-space sc-bad-sp \ | ||
| 8 | sc-bad-arg sc-boundary sc-boundary-2 halt exit create-normal \ | ||
| 9 | create-empty create-null create-bad-ptr create-long create-exists \ | ||
| 10 | create-bound open-normal open-missing open-boundary open-empty \ | ||
| 11 | open-null open-bad-ptr open-twice close-normal close-twice close-stdin \ | ||
| 12 | close-stdout close-bad-fd read-normal read-bad-ptr read-boundary \ | ||
| 13 | read-zero read-stdout read-bad-fd write-normal write-bad-ptr \ | ||
| 14 | write-boundary write-zero write-stdin write-bad-fd exec-once exec-arg \ | ||
| 15 | exec-multiple exec-missing exec-bad-ptr wait-simple wait-twice \ | ||
| 16 | wait-killed wait-bad-pid multi-recurse multi-child-fd rox-simple \ | ||
| 17 | rox-child rox-multichild bad-read bad-write bad-read2 bad-write2 \ | ||
| 18 | bad-jump bad-jump2) | ||
| 19 | |||
| 20 | tests/userprog_PROGS = $(tests/userprog_TESTS) $(addprefix \ | ||
| 21 | tests/userprog/,child-simple child-args child-bad child-close child-rox) | ||
| 22 | |||
| 23 | tests/userprog/args-none_SRC = tests/userprog/args.c | ||
| 24 | tests/userprog/args-single_SRC = tests/userprog/args.c | ||
| 25 | tests/userprog/args-multiple_SRC = tests/userprog/args.c | ||
| 26 | tests/userprog/args-many_SRC = tests/userprog/args.c | ||
| 27 | tests/userprog/args-dbl-space_SRC = tests/userprog/args.c | ||
| 28 | tests/userprog/sc-bad-sp_SRC = tests/userprog/sc-bad-sp.c tests/main.c | ||
| 29 | tests/userprog/sc-bad-arg_SRC = tests/userprog/sc-bad-arg.c tests/main.c | ||
| 30 | tests/userprog/bad-read_SRC = tests/userprog/bad-read.c tests/main.c | ||
| 31 | tests/userprog/bad-write_SRC = tests/userprog/bad-write.c tests/main.c | ||
| 32 | tests/userprog/bad-jump_SRC = tests/userprog/bad-jump.c tests/main.c | ||
| 33 | tests/userprog/bad-read2_SRC = tests/userprog/bad-read2.c tests/main.c | ||
| 34 | tests/userprog/bad-write2_SRC = tests/userprog/bad-write2.c tests/main.c | ||
| 35 | tests/userprog/bad-jump2_SRC = tests/userprog/bad-jump2.c tests/main.c | ||
| 36 | tests/userprog/sc-boundary_SRC = tests/userprog/sc-boundary.c \ | ||
| 37 | tests/userprog/boundary.c tests/main.c | ||
| 38 | tests/userprog/sc-boundary-2_SRC = tests/userprog/sc-boundary-2.c \ | ||
| 39 | tests/userprog/boundary.c tests/main.c | ||
| 40 | tests/userprog/halt_SRC = tests/userprog/halt.c tests/main.c | ||
| 41 | tests/userprog/exit_SRC = tests/userprog/exit.c tests/main.c | ||
| 42 | tests/userprog/create-normal_SRC = tests/userprog/create-normal.c tests/main.c | ||
| 43 | tests/userprog/create-empty_SRC = tests/userprog/create-empty.c tests/main.c | ||
| 44 | tests/userprog/create-null_SRC = tests/userprog/create-null.c tests/main.c | ||
| 45 | tests/userprog/create-bad-ptr_SRC = tests/userprog/create-bad-ptr.c \ | ||
| 46 | tests/main.c | ||
| 47 | tests/userprog/create-long_SRC = tests/userprog/create-long.c tests/main.c | ||
| 48 | tests/userprog/create-exists_SRC = tests/userprog/create-exists.c tests/main.c | ||
| 49 | tests/userprog/create-bound_SRC = tests/userprog/create-bound.c \ | ||
| 50 | tests/userprog/boundary.c tests/main.c | ||
| 51 | tests/userprog/open-normal_SRC = tests/userprog/open-normal.c tests/main.c | ||
| 52 | tests/userprog/open-missing_SRC = tests/userprog/open-missing.c tests/main.c | ||
| 53 | tests/userprog/open-boundary_SRC = tests/userprog/open-boundary.c \ | ||
| 54 | tests/userprog/boundary.c tests/main.c | ||
| 55 | tests/userprog/open-empty_SRC = tests/userprog/open-empty.c tests/main.c | ||
| 56 | tests/userprog/open-null_SRC = tests/userprog/open-null.c tests/main.c | ||
| 57 | tests/userprog/open-bad-ptr_SRC = tests/userprog/open-bad-ptr.c tests/main.c | ||
| 58 | tests/userprog/open-twice_SRC = tests/userprog/open-twice.c tests/main.c | ||
| 59 | tests/userprog/close-normal_SRC = tests/userprog/close-normal.c tests/main.c | ||
| 60 | tests/userprog/close-twice_SRC = tests/userprog/close-twice.c tests/main.c | ||
| 61 | tests/userprog/close-stdin_SRC = tests/userprog/close-stdin.c tests/main.c | ||
| 62 | tests/userprog/close-stdout_SRC = tests/userprog/close-stdout.c tests/main.c | ||
| 63 | tests/userprog/close-bad-fd_SRC = tests/userprog/close-bad-fd.c tests/main.c | ||
| 64 | tests/userprog/read-normal_SRC = tests/userprog/read-normal.c tests/main.c | ||
| 65 | tests/userprog/read-bad-ptr_SRC = tests/userprog/read-bad-ptr.c tests/main.c | ||
| 66 | tests/userprog/read-boundary_SRC = tests/userprog/read-boundary.c \ | ||
| 67 | tests/userprog/boundary.c tests/main.c | ||
| 68 | tests/userprog/read-zero_SRC = tests/userprog/read-zero.c tests/main.c | ||
| 69 | tests/userprog/read-stdout_SRC = tests/userprog/read-stdout.c tests/main.c | ||
| 70 | tests/userprog/read-bad-fd_SRC = tests/userprog/read-bad-fd.c tests/main.c | ||
| 71 | tests/userprog/write-normal_SRC = tests/userprog/write-normal.c tests/main.c | ||
| 72 | tests/userprog/write-bad-ptr_SRC = tests/userprog/write-bad-ptr.c tests/main.c | ||
| 73 | tests/userprog/write-boundary_SRC = tests/userprog/write-boundary.c \ | ||
| 74 | tests/userprog/boundary.c tests/main.c | ||
| 75 | tests/userprog/write-zero_SRC = tests/userprog/write-zero.c tests/main.c | ||
| 76 | tests/userprog/write-stdin_SRC = tests/userprog/write-stdin.c tests/main.c | ||
| 77 | tests/userprog/write-bad-fd_SRC = tests/userprog/write-bad-fd.c tests/main.c | ||
| 78 | tests/userprog/exec-once_SRC = tests/userprog/exec-once.c tests/main.c | ||
| 79 | tests/userprog/exec-arg_SRC = tests/userprog/exec-arg.c tests/main.c | ||
| 80 | tests/userprog/exec-multiple_SRC = tests/userprog/exec-multiple.c tests/main.c | ||
| 81 | tests/userprog/exec-missing_SRC = tests/userprog/exec-missing.c tests/main.c | ||
| 82 | tests/userprog/exec-bad-ptr_SRC = tests/userprog/exec-bad-ptr.c tests/main.c | ||
| 83 | tests/userprog/wait-simple_SRC = tests/userprog/wait-simple.c tests/main.c | ||
| 84 | tests/userprog/wait-twice_SRC = tests/userprog/wait-twice.c tests/main.c | ||
| 85 | tests/userprog/wait-killed_SRC = tests/userprog/wait-killed.c tests/main.c | ||
| 86 | tests/userprog/wait-bad-pid_SRC = tests/userprog/wait-bad-pid.c tests/main.c | ||
| 87 | tests/userprog/multi-recurse_SRC = tests/userprog/multi-recurse.c | ||
| 88 | tests/userprog/multi-child-fd_SRC = tests/userprog/multi-child-fd.c \ | ||
| 89 | tests/main.c | ||
| 90 | tests/userprog/rox-simple_SRC = tests/userprog/rox-simple.c tests/main.c | ||
| 91 | tests/userprog/rox-child_SRC = tests/userprog/rox-child.c tests/main.c | ||
| 92 | tests/userprog/rox-multichild_SRC = tests/userprog/rox-multichild.c \ | ||
| 93 | tests/main.c | ||
| 94 | |||
| 95 | tests/userprog/child-simple_SRC = tests/userprog/child-simple.c | ||
| 96 | tests/userprog/child-args_SRC = tests/userprog/args.c | ||
| 97 | tests/userprog/child-bad_SRC = tests/userprog/child-bad.c tests/main.c | ||
| 98 | tests/userprog/child-close_SRC = tests/userprog/child-close.c | ||
| 99 | tests/userprog/child-rox_SRC = tests/userprog/child-rox.c | ||
| 100 | |||
| 101 | $(foreach prog,$(tests/userprog_PROGS),$(eval $(prog)_SRC += tests/lib.c)) | ||
| 102 | |||
| 103 | tests/userprog/args-single_ARGS = onearg | ||
| 104 | tests/userprog/args-multiple_ARGS = some arguments for you! | ||
| 105 | tests/userprog/args-many_ARGS = a b c d e f g h i j k l m n o p q r s t u v | ||
| 106 | tests/userprog/args-dbl-space_ARGS = two spaces! | ||
| 107 | tests/userprog/multi-recurse_ARGS = 15 | ||
| 108 | |||
| 109 | tests/userprog/open-normal_PUTFILES += tests/userprog/sample.txt | ||
| 110 | tests/userprog/open-boundary_PUTFILES += tests/userprog/sample.txt | ||
| 111 | tests/userprog/open-twice_PUTFILES += tests/userprog/sample.txt | ||
| 112 | tests/userprog/close-normal_PUTFILES += tests/userprog/sample.txt | ||
| 113 | tests/userprog/close-twice_PUTFILES += tests/userprog/sample.txt | ||
| 114 | tests/userprog/read-normal_PUTFILES += tests/userprog/sample.txt | ||
| 115 | tests/userprog/read-bad-ptr_PUTFILES += tests/userprog/sample.txt | ||
| 116 | tests/userprog/read-boundary_PUTFILES += tests/userprog/sample.txt | ||
| 117 | tests/userprog/read-zero_PUTFILES += tests/userprog/sample.txt | ||
| 118 | tests/userprog/write-normal_PUTFILES += tests/userprog/sample.txt | ||
| 119 | tests/userprog/write-bad-ptr_PUTFILES += tests/userprog/sample.txt | ||
| 120 | tests/userprog/write-boundary_PUTFILES += tests/userprog/sample.txt | ||
| 121 | tests/userprog/write-zero_PUTFILES += tests/userprog/sample.txt | ||
| 122 | tests/userprog/multi-child-fd_PUTFILES += tests/userprog/sample.txt | ||
| 123 | |||
| 124 | tests/userprog/exec-once_PUTFILES += tests/userprog/child-simple | ||
| 125 | tests/userprog/exec-multiple_PUTFILES += tests/userprog/child-simple | ||
| 126 | tests/userprog/wait-simple_PUTFILES += tests/userprog/child-simple | ||
| 127 | tests/userprog/wait-twice_PUTFILES += tests/userprog/child-simple | ||
| 128 | |||
| 129 | tests/userprog/exec-arg_PUTFILES += tests/userprog/child-args | ||
| 130 | tests/userprog/multi-child-fd_PUTFILES += tests/userprog/child-close | ||
| 131 | tests/userprog/wait-killed_PUTFILES += tests/userprog/child-bad | ||
| 132 | tests/userprog/rox-child_PUTFILES += tests/userprog/child-rox | ||
| 133 | tests/userprog/rox-multichild_PUTFILES += tests/userprog/child-rox | ||
diff --git a/pintos-progos/tests/userprog/Rubric.functionality b/pintos-progos/tests/userprog/Rubric.functionality new file mode 100644 index 0000000..ea76c44 --- /dev/null +++ b/pintos-progos/tests/userprog/Rubric.functionality | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | Functionality of system calls: | ||
| 2 | - Test argument passing on Pintos command line. | ||
| 3 | 3 args-none | ||
| 4 | 3 args-single | ||
| 5 | 3 args-multiple | ||
| 6 | 3 args-many | ||
| 7 | 3 args-dbl-space | ||
| 8 | |||
| 9 | - Test "create" system call. | ||
| 10 | 3 create-empty | ||
| 11 | 3 create-long | ||
| 12 | 3 create-normal | ||
| 13 | 3 create-exists | ||
| 14 | |||
| 15 | - Test "open" system call. | ||
| 16 | 3 open-missing | ||
| 17 | 3 open-normal | ||
| 18 | 3 open-twice | ||
| 19 | |||
| 20 | - Test "read" system call. | ||
| 21 | 3 read-normal | ||
| 22 | 3 read-zero | ||
| 23 | |||
| 24 | - Test "write" system call. | ||
| 25 | 3 write-normal | ||
| 26 | 3 write-zero | ||
| 27 | |||
| 28 | - Test "close" system call. | ||
| 29 | 3 close-normal | ||
| 30 | |||
| 31 | - Test "exec" system call. | ||
| 32 | 5 exec-once | ||
| 33 | 5 exec-multiple | ||
| 34 | 5 exec-arg | ||
| 35 | |||
| 36 | - Test "wait" system call. | ||
| 37 | 5 wait-simple | ||
| 38 | 5 wait-twice | ||
| 39 | |||
| 40 | - Test "exit" system call. | ||
| 41 | 5 exit | ||
| 42 | |||
| 43 | - Test "halt" system call. | ||
| 44 | 3 halt | ||
| 45 | |||
| 46 | - Test recursive execution of user programs. | ||
| 47 | 15 multi-recurse | ||
| 48 | |||
| 49 | - Test read-only executable feature. | ||
| 50 | 3 rox-simple | ||
| 51 | 3 rox-child | ||
| 52 | 3 rox-multichild | ||
diff --git a/pintos-progos/tests/userprog/Rubric.robustness b/pintos-progos/tests/userprog/Rubric.robustness new file mode 100644 index 0000000..b7d1035 --- /dev/null +++ b/pintos-progos/tests/userprog/Rubric.robustness | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | Robustness of system calls: | ||
| 2 | - Test robustness of file descriptor handling. | ||
| 3 | 2 close-stdin | ||
| 4 | 2 close-stdout | ||
| 5 | 2 close-bad-fd | ||
| 6 | 2 close-twice | ||
| 7 | 2 read-bad-fd | ||
| 8 | 2 read-stdout | ||
| 9 | 2 write-bad-fd | ||
| 10 | 2 write-stdin | ||
| 11 | 2 multi-child-fd | ||
| 12 | |||
| 13 | - Test robustness of pointer handling. | ||
| 14 | 3 create-bad-ptr | ||
| 15 | 3 exec-bad-ptr | ||
| 16 | 3 open-bad-ptr | ||
| 17 | 3 read-bad-ptr | ||
| 18 | 3 write-bad-ptr | ||
| 19 | |||
| 20 | - Test robustness of buffer copying across page boundaries. | ||
| 21 | 3 create-bound | ||
| 22 | 3 open-boundary | ||
| 23 | 3 read-boundary | ||
| 24 | 3 write-boundary | ||
| 25 | |||
| 26 | - Test handling of null pointer and empty strings. | ||
| 27 | 2 create-null | ||
| 28 | 2 open-null | ||
| 29 | 2 open-empty | ||
| 30 | |||
| 31 | - Test robustness of system call implementation. | ||
| 32 | 3 sc-bad-arg | ||
| 33 | 3 sc-bad-sp | ||
| 34 | 5 sc-boundary | ||
| 35 | 5 sc-boundary-2 | ||
| 36 | |||
| 37 | - Test robustness of "exec" and "wait" system calls. | ||
| 38 | 5 exec-missing | ||
| 39 | 5 wait-bad-pid | ||
| 40 | 5 wait-killed | ||
| 41 | |||
| 42 | - Test robustness of exception handling. | ||
| 43 | 1 bad-read | ||
| 44 | 1 bad-write | ||
| 45 | 1 bad-jump | ||
| 46 | 1 bad-read2 | ||
| 47 | 1 bad-write2 | ||
| 48 | 1 bad-jump2 | ||
diff --git a/pintos-progos/tests/userprog/args-dbl-space.ck b/pintos-progos/tests/userprog/args-dbl-space.ck new file mode 100644 index 0000000..dfbcf4b --- /dev/null +++ b/pintos-progos/tests/userprog/args-dbl-space.ck | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (args) begin | ||
| 7 | (args) argc = 3 | ||
| 8 | (args) argv[0] = 'args-dbl-space' | ||
| 9 | (args) argv[1] = 'two' | ||
| 10 | (args) argv[2] = 'spaces!' | ||
| 11 | (args) argv[3] = null | ||
| 12 | (args) end | ||
| 13 | args-dbl-space: exit(0) | ||
| 14 | EOF | ||
| 15 | pass; | ||
diff --git a/pintos-progos/tests/userprog/args-many.ck b/pintos-progos/tests/userprog/args-many.ck new file mode 100644 index 0000000..214574a --- /dev/null +++ b/pintos-progos/tests/userprog/args-many.ck | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (args) begin | ||
| 7 | (args) argc = 23 | ||
| 8 | (args) argv[0] = 'args-many' | ||
| 9 | (args) argv[1] = 'a' | ||
| 10 | (args) argv[2] = 'b' | ||
| 11 | (args) argv[3] = 'c' | ||
| 12 | (args) argv[4] = 'd' | ||
| 13 | (args) argv[5] = 'e' | ||
| 14 | (args) argv[6] = 'f' | ||
| 15 | (args) argv[7] = 'g' | ||
| 16 | (args) argv[8] = 'h' | ||
| 17 | (args) argv[9] = 'i' | ||
| 18 | (args) argv[10] = 'j' | ||
| 19 | (args) argv[11] = 'k' | ||
| 20 | (args) argv[12] = 'l' | ||
| 21 | (args) argv[13] = 'm' | ||
| 22 | (args) argv[14] = 'n' | ||
| 23 | (args) argv[15] = 'o' | ||
| 24 | (args) argv[16] = 'p' | ||
| 25 | (args) argv[17] = 'q' | ||
| 26 | (args) argv[18] = 'r' | ||
| 27 | (args) argv[19] = 's' | ||
| 28 | (args) argv[20] = 't' | ||
| 29 | (args) argv[21] = 'u' | ||
| 30 | (args) argv[22] = 'v' | ||
| 31 | (args) argv[23] = null | ||
| 32 | (args) end | ||
| 33 | args-many: exit(0) | ||
| 34 | EOF | ||
| 35 | pass; | ||
diff --git a/pintos-progos/tests/userprog/args-multiple.ck b/pintos-progos/tests/userprog/args-multiple.ck new file mode 100644 index 0000000..227e6cc --- /dev/null +++ b/pintos-progos/tests/userprog/args-multiple.ck | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (args) begin | ||
| 7 | (args) argc = 5 | ||
| 8 | (args) argv[0] = 'args-multiple' | ||
| 9 | (args) argv[1] = 'some' | ||
| 10 | (args) argv[2] = 'arguments' | ||
| 11 | (args) argv[3] = 'for' | ||
| 12 | (args) argv[4] = 'you!' | ||
| 13 | (args) argv[5] = null | ||
| 14 | (args) end | ||
| 15 | args-multiple: exit(0) | ||
| 16 | EOF | ||
| 17 | pass; | ||
diff --git a/pintos-progos/tests/userprog/args-none.ck b/pintos-progos/tests/userprog/args-none.ck new file mode 100644 index 0000000..146318e --- /dev/null +++ b/pintos-progos/tests/userprog/args-none.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (args) begin | ||
| 7 | (args) argc = 1 | ||
| 8 | (args) argv[0] = 'args-none' | ||
| 9 | (args) argv[1] = null | ||
| 10 | (args) end | ||
| 11 | args-none: exit(0) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/args-single.ck b/pintos-progos/tests/userprog/args-single.ck new file mode 100644 index 0000000..24582b4 --- /dev/null +++ b/pintos-progos/tests/userprog/args-single.ck | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (args) begin | ||
| 7 | (args) argc = 2 | ||
| 8 | (args) argv[0] = 'args-single' | ||
| 9 | (args) argv[1] = 'onearg' | ||
| 10 | (args) argv[2] = null | ||
| 11 | (args) end | ||
| 12 | args-single: exit(0) | ||
| 13 | EOF | ||
| 14 | pass; | ||
diff --git a/pintos-progos/tests/userprog/args.c b/pintos-progos/tests/userprog/args.c new file mode 100644 index 0000000..20eda44 --- /dev/null +++ b/pintos-progos/tests/userprog/args.c | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | /* Prints the command-line arguments. | ||
| 2 | This program is used for all of the args-* tests. Grading is | ||
| 3 | done differently for each of the args-* tests based on the | ||
| 4 | output. */ | ||
| 5 | |||
| 6 | #include "tests/lib.h" | ||
| 7 | |||
| 8 | int | ||
| 9 | main (int argc, char *argv[]) | ||
| 10 | { | ||
| 11 | int i; | ||
| 12 | |||
| 13 | test_name = "args"; | ||
| 14 | |||
| 15 | msg ("begin"); | ||
| 16 | msg ("argc = %d", argc); | ||
| 17 | for (i = 0; i <= argc; i++) | ||
| 18 | if (argv[i] != NULL) | ||
| 19 | msg ("argv[%d] = '%s'", i, argv[i]); | ||
| 20 | else | ||
| 21 | msg ("argv[%d] = null", i); | ||
| 22 | msg ("end"); | ||
| 23 | |||
| 24 | return 0; | ||
| 25 | } | ||
diff --git a/pintos-progos/tests/userprog/bad-jump.c b/pintos-progos/tests/userprog/bad-jump.c new file mode 100644 index 0000000..51b7c9f --- /dev/null +++ b/pintos-progos/tests/userprog/bad-jump.c | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | /* This program attempts to execute code at address 0, which is not mapped. | ||
| 2 | This should terminate the process with a -1 exit code. */ | ||
| 3 | |||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | msg ("Congratulations - you have successfully called NULL: %d", | ||
| 11 | ((int (*)(void))NULL)()); | ||
| 12 | fail ("should have exited with -1"); | ||
| 13 | } | ||
diff --git a/pintos-progos/tests/userprog/bad-jump.ck b/pintos-progos/tests/userprog/bad-jump.ck new file mode 100644 index 0000000..e1c178b --- /dev/null +++ b/pintos-progos/tests/userprog/bad-jump.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']); | ||
| 6 | (bad-jump) begin | ||
| 7 | bad-jump: exit(-1) | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/userprog/bad-jump2.c b/pintos-progos/tests/userprog/bad-jump2.c new file mode 100644 index 0000000..dc7c2a7 --- /dev/null +++ b/pintos-progos/tests/userprog/bad-jump2.c | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | /* This program attempts to execute code at a kernel virtual address. | ||
| 2 | This should terminate the process with a -1 exit code. */ | ||
| 3 | |||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | msg ("Congratulations - you have successfully called kernel code: %d", | ||
| 11 | ((int (*)(void))0xC0000000)()); | ||
| 12 | fail ("should have exited with -1"); | ||
| 13 | } | ||
diff --git a/pintos-progos/tests/userprog/bad-jump2.ck b/pintos-progos/tests/userprog/bad-jump2.ck new file mode 100644 index 0000000..35f0f97 --- /dev/null +++ b/pintos-progos/tests/userprog/bad-jump2.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']); | ||
| 6 | (bad-jump2) begin | ||
| 7 | bad-jump2: exit(-1) | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/userprog/bad-read.c b/pintos-progos/tests/userprog/bad-read.c new file mode 100644 index 0000000..904c278 --- /dev/null +++ b/pintos-progos/tests/userprog/bad-read.c | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | /* This program attempts to read memory at an address that is not mapped. | ||
| 2 | This should terminate the process with a -1 exit code. */ | ||
| 3 | |||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | msg ("Congratulations - you have successfully dereferenced NULL: %d", | ||
| 11 | *(int *)NULL); | ||
| 12 | fail ("should have exited with -1"); | ||
| 13 | } | ||
diff --git a/pintos-progos/tests/userprog/bad-read.ck b/pintos-progos/tests/userprog/bad-read.ck new file mode 100644 index 0000000..4d4d926 --- /dev/null +++ b/pintos-progos/tests/userprog/bad-read.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']); | ||
| 6 | (bad-read) begin | ||
| 7 | bad-read: exit(-1) | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/userprog/bad-read2.c b/pintos-progos/tests/userprog/bad-read2.c new file mode 100644 index 0000000..a2fc237 --- /dev/null +++ b/pintos-progos/tests/userprog/bad-read2.c | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | /* This program attempts to read kernel memory. | ||
| 2 | This should terminate the process with a -1 exit code. */ | ||
| 3 | |||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | msg ("Congratulations - you have successfully read kernel memory: %d", | ||
| 11 | *(int *)0xC0000000); | ||
| 12 | fail ("should have exited with -1"); | ||
| 13 | } | ||
diff --git a/pintos-progos/tests/userprog/bad-read2.ck b/pintos-progos/tests/userprog/bad-read2.ck new file mode 100644 index 0000000..fa27c7d --- /dev/null +++ b/pintos-progos/tests/userprog/bad-read2.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']); | ||
| 6 | (bad-read2) begin | ||
| 7 | bad-read2: exit(-1) | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/userprog/bad-write.c b/pintos-progos/tests/userprog/bad-write.c new file mode 100644 index 0000000..000c26b --- /dev/null +++ b/pintos-progos/tests/userprog/bad-write.c | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | /* This program attempts to write to memory at an address that is not mapped. | ||
| 2 | This should terminate the process with a -1 exit code. */ | ||
| 3 | |||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | *(int *)NULL = 42; | ||
| 11 | fail ("should have exited with -1"); | ||
| 12 | } | ||
diff --git a/pintos-progos/tests/userprog/bad-write.ck b/pintos-progos/tests/userprog/bad-write.ck new file mode 100644 index 0000000..d213b49 --- /dev/null +++ b/pintos-progos/tests/userprog/bad-write.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']); | ||
| 6 | (bad-write) begin | ||
| 7 | bad-write: exit(-1) | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/userprog/bad-write2.c b/pintos-progos/tests/userprog/bad-write2.c new file mode 100644 index 0000000..753da1e --- /dev/null +++ b/pintos-progos/tests/userprog/bad-write2.c | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | /* This program attempts to write to kernel memory. | ||
| 2 | This should terminate the process with a -1 exit code. */ | ||
| 3 | |||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | *(int *)0xC0000000 = 42; | ||
| 11 | fail ("should have exited with -1"); | ||
| 12 | } | ||
diff --git a/pintos-progos/tests/userprog/bad-write2.ck b/pintos-progos/tests/userprog/bad-write2.ck new file mode 100644 index 0000000..c6a3420 --- /dev/null +++ b/pintos-progos/tests/userprog/bad-write2.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']); | ||
| 6 | (bad-write2) begin | ||
| 7 | bad-write2: exit(-1) | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/userprog/boundary.c b/pintos-progos/tests/userprog/boundary.c new file mode 100644 index 0000000..59907ec --- /dev/null +++ b/pintos-progos/tests/userprog/boundary.c | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | /* Utility function for tests that try to break system calls by | ||
| 2 | passing them data that crosses from one virtual page to | ||
| 3 | another. */ | ||
| 4 | |||
| 5 | #include <inttypes.h> | ||
| 6 | #include <round.h> | ||
| 7 | #include <string.h> | ||
| 8 | #include "tests/userprog/boundary.h" | ||
| 9 | |||
| 10 | static char dst[8192]; | ||
| 11 | |||
| 12 | /* Returns the beginning of a page. There are at least 2048 | ||
| 13 | modifiable bytes on either side of the pointer returned. */ | ||
| 14 | void * | ||
| 15 | get_boundary_area (void) | ||
| 16 | { | ||
| 17 | char *p = (char *) ROUND_UP ((uintptr_t) dst, 4096); | ||
| 18 | if (p - dst < 2048) | ||
| 19 | p += 4096; | ||
| 20 | return p; | ||
| 21 | } | ||
| 22 | |||
| 23 | /* Returns a copy of SRC split across the boundary between two | ||
| 24 | pages. */ | ||
| 25 | char * | ||
| 26 | copy_string_across_boundary (const char *src) | ||
| 27 | { | ||
| 28 | char *p = get_boundary_area (); | ||
| 29 | p -= strlen (src) < 4096 ? strlen (src) / 2 : 4096; | ||
| 30 | strlcpy (p, src, 4096); | ||
| 31 | return p; | ||
| 32 | } | ||
| 33 | |||
diff --git a/pintos-progos/tests/userprog/boundary.h b/pintos-progos/tests/userprog/boundary.h new file mode 100644 index 0000000..c8e4b3b --- /dev/null +++ b/pintos-progos/tests/userprog/boundary.h | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | #ifndef TESTS_USERPROG_BOUNDARY_H | ||
| 2 | #define TESTS_USERPROG_BOUNDARY_H | ||
| 3 | |||
| 4 | void *get_boundary_area (void); | ||
| 5 | char *copy_string_across_boundary (const char *); | ||
| 6 | |||
| 7 | #endif /* tests/userprog/boundary.h */ | ||
diff --git a/pintos-progos/tests/userprog/child-bad.c b/pintos-progos/tests/userprog/child-bad.c new file mode 100644 index 0000000..77d7a69 --- /dev/null +++ b/pintos-progos/tests/userprog/child-bad.c | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | /* Child process run by wait-killed test. | ||
| 2 | Sets the stack pointer (%esp) to an invalid value and invokes | ||
| 3 | a system call, which should then terminate the process with a | ||
| 4 | -1 exit code. */ | ||
| 5 | |||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | asm volatile ("movl $0x20101234, %esp; int $0x30"); | ||
| 13 | fail ("should have exited with -1"); | ||
| 14 | } | ||
diff --git a/pintos-progos/tests/userprog/child-close.c b/pintos-progos/tests/userprog/child-close.c new file mode 100644 index 0000000..ac948c8 --- /dev/null +++ b/pintos-progos/tests/userprog/child-close.c | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | /* Child process run by multi-child-fd test. | ||
| 2 | |||
| 3 | Attempts to close the file descriptor passed as the first | ||
| 4 | command-line argument. This is invalid, because file | ||
| 5 | descriptors are not inherited in Pintos. Two results are | ||
| 6 | allowed: either the system call should return without taking | ||
| 7 | any action, or the kernel should terminate the process with a | ||
| 8 | -1 exit code. */ | ||
| 9 | |||
| 10 | #include <ctype.h> | ||
| 11 | #include <stdio.h> | ||
| 12 | #include <stdlib.h> | ||
| 13 | #include <syscall.h> | ||
| 14 | #include "tests/lib.h" | ||
| 15 | |||
| 16 | const char *test_name = "child-close"; | ||
| 17 | |||
| 18 | int | ||
| 19 | main (int argc UNUSED, char *argv[]) | ||
| 20 | { | ||
| 21 | msg ("begin"); | ||
| 22 | if (!isdigit (*argv[1])) | ||
| 23 | fail ("bad command-line arguments"); | ||
| 24 | close (atoi (argv[1])); | ||
| 25 | msg ("end"); | ||
| 26 | |||
| 27 | return 0; | ||
| 28 | } | ||
diff --git a/pintos-progos/tests/userprog/child-rox.c b/pintos-progos/tests/userprog/child-rox.c new file mode 100644 index 0000000..aba808b --- /dev/null +++ b/pintos-progos/tests/userprog/child-rox.c | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | /* Child process run by rox-child and rox-multichild tests. | ||
| 2 | Opens and tries to write to its own executable, verifying that | ||
| 3 | that is disallowed. | ||
| 4 | Then recursively executes itself to the depth indicated by the | ||
| 5 | first command-line argument. */ | ||
| 6 | |||
| 7 | #include <ctype.h> | ||
| 8 | #include <stdio.h> | ||
| 9 | #include <stdlib.h> | ||
| 10 | #include <syscall.h> | ||
| 11 | #include "tests/lib.h" | ||
| 12 | |||
| 13 | const char *test_name = "child-rox"; | ||
| 14 | |||
| 15 | static void | ||
| 16 | try_write (void) | ||
| 17 | { | ||
| 18 | int handle; | ||
| 19 | char buffer[19]; | ||
| 20 | |||
| 21 | quiet = true; | ||
| 22 | CHECK ((handle = open ("child-rox")) > 1, "open \"child-rox\""); | ||
| 23 | quiet = false; | ||
| 24 | |||
| 25 | CHECK (write (handle, buffer, sizeof buffer) == 0, | ||
| 26 | "try to write \"child-rox\""); | ||
| 27 | |||
| 28 | close (handle); | ||
| 29 | } | ||
| 30 | |||
| 31 | int | ||
| 32 | main (int argc UNUSED, char *argv[]) | ||
| 33 | { | ||
| 34 | msg ("begin"); | ||
| 35 | try_write (); | ||
| 36 | |||
| 37 | if (!isdigit (*argv[1])) | ||
| 38 | fail ("bad command-line arguments"); | ||
| 39 | if (atoi (argv[1]) > 1) | ||
| 40 | { | ||
| 41 | char cmd[128]; | ||
| 42 | int child; | ||
| 43 | |||
| 44 | snprintf (cmd, sizeof cmd, "child-rox %d", atoi (argv[1]) - 1); | ||
| 45 | CHECK ((child = exec (cmd)) != -1, "exec \"%s\"", cmd); | ||
| 46 | quiet = true; | ||
| 47 | CHECK (wait (child) == 12, "wait for \"child-rox\""); | ||
| 48 | quiet = false; | ||
| 49 | } | ||
| 50 | |||
| 51 | try_write (); | ||
| 52 | msg ("end"); | ||
| 53 | |||
| 54 | return 12; | ||
| 55 | } | ||
diff --git a/pintos-progos/tests/userprog/child-simple.c b/pintos-progos/tests/userprog/child-simple.c new file mode 100644 index 0000000..0d2dacf --- /dev/null +++ b/pintos-progos/tests/userprog/child-simple.c | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | /* Child process run by exec-multiple, exec-one, wait-simple, and | ||
| 2 | wait-twice tests. | ||
| 3 | Just prints a single message and terminates. */ | ||
| 4 | |||
| 5 | #include <stdio.h> | ||
| 6 | #include "tests/lib.h" | ||
| 7 | |||
| 8 | const char *test_name = "child-simple"; | ||
| 9 | |||
| 10 | int | ||
| 11 | main (void) | ||
| 12 | { | ||
| 13 | msg ("run"); | ||
| 14 | return 81; | ||
| 15 | } | ||
diff --git a/pintos-progos/tests/userprog/close-bad-fd.c b/pintos-progos/tests/userprog/close-bad-fd.c new file mode 100644 index 0000000..f63bb9a --- /dev/null +++ b/pintos-progos/tests/userprog/close-bad-fd.c | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | /* Tries to close an invalid fd, which must either fail silently | ||
| 2 | or terminate with exit code -1. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | close (0x20101234); | ||
| 11 | } | ||
diff --git a/pintos-progos/tests/userprog/close-bad-fd.ck b/pintos-progos/tests/userprog/close-bad-fd.ck new file mode 100644 index 0000000..497b17c --- /dev/null +++ b/pintos-progos/tests/userprog/close-bad-fd.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (close-bad-fd) begin | ||
| 7 | (close-bad-fd) end | ||
| 8 | close-bad-fd: exit(0) | ||
| 9 | EOF | ||
| 10 | (close-bad-fd) begin | ||
| 11 | close-bad-fd: exit(-1) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/close-normal.c b/pintos-progos/tests/userprog/close-normal.c new file mode 100644 index 0000000..8ce04e3 --- /dev/null +++ b/pintos-progos/tests/userprog/close-normal.c | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | /* Opens a file and then closes it. */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | int handle; | ||
| 11 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 12 | msg ("close \"sample.txt\""); | ||
| 13 | close (handle); | ||
| 14 | } | ||
diff --git a/pintos-progos/tests/userprog/close-normal.ck b/pintos-progos/tests/userprog/close-normal.ck new file mode 100644 index 0000000..fe41342 --- /dev/null +++ b/pintos-progos/tests/userprog/close-normal.ck | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (close-normal) begin | ||
| 7 | (close-normal) open "sample.txt" | ||
| 8 | (close-normal) close "sample.txt" | ||
| 9 | (close-normal) end | ||
| 10 | close-normal: exit(0) | ||
| 11 | EOF | ||
| 12 | pass; | ||
diff --git a/pintos-progos/tests/userprog/close-stdin.c b/pintos-progos/tests/userprog/close-stdin.c new file mode 100644 index 0000000..9bbf9f2 --- /dev/null +++ b/pintos-progos/tests/userprog/close-stdin.c | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | /* Tries to close the keyboard input stream, which must either | ||
| 2 | fail silently or terminate with exit code -1. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | close (0); | ||
| 11 | } | ||
diff --git a/pintos-progos/tests/userprog/close-stdin.ck b/pintos-progos/tests/userprog/close-stdin.ck new file mode 100644 index 0000000..3d28507 --- /dev/null +++ b/pintos-progos/tests/userprog/close-stdin.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (close-stdin) begin | ||
| 7 | (close-stdin) end | ||
| 8 | close-stdin: exit(0) | ||
| 9 | EOF | ||
| 10 | (close-stdin) begin | ||
| 11 | close-stdin: exit(-1) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/close-stdout.c b/pintos-progos/tests/userprog/close-stdout.c new file mode 100644 index 0000000..886523f --- /dev/null +++ b/pintos-progos/tests/userprog/close-stdout.c | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | /* Tries to close the console output stream, which must either | ||
| 2 | fail silently or terminate with exit code -1. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | close (1); | ||
| 11 | } | ||
diff --git a/pintos-progos/tests/userprog/close-stdout.ck b/pintos-progos/tests/userprog/close-stdout.ck new file mode 100644 index 0000000..3cbbcff --- /dev/null +++ b/pintos-progos/tests/userprog/close-stdout.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (close-stdout) begin | ||
| 7 | (close-stdout) end | ||
| 8 | close-stdout: exit(0) | ||
| 9 | EOF | ||
| 10 | (close-stdout) begin | ||
| 11 | close-stdout: exit(-1) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/close-twice.c b/pintos-progos/tests/userprog/close-twice.c new file mode 100644 index 0000000..830bccf --- /dev/null +++ b/pintos-progos/tests/userprog/close-twice.c | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | /* Opens a file and then tries to close it twice. The second | ||
| 2 | close must either fail silently or terminate with exit code | ||
| 3 | -1. */ | ||
| 4 | |||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | int handle; | ||
| 13 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 14 | msg ("close \"sample.txt\""); | ||
| 15 | close (handle); | ||
| 16 | msg ("close \"sample.txt\" again"); | ||
| 17 | close (handle); | ||
| 18 | } | ||
diff --git a/pintos-progos/tests/userprog/close-twice.ck b/pintos-progos/tests/userprog/close-twice.ck new file mode 100644 index 0000000..deb55a6 --- /dev/null +++ b/pintos-progos/tests/userprog/close-twice.ck | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (close-twice) begin | ||
| 7 | (close-twice) open "sample.txt" | ||
| 8 | (close-twice) close "sample.txt" | ||
| 9 | (close-twice) close "sample.txt" again | ||
| 10 | (close-twice) end | ||
| 11 | close-twice: exit(0) | ||
| 12 | EOF | ||
| 13 | (close-twice) begin | ||
| 14 | (close-twice) open "sample.txt" | ||
| 15 | (close-twice) close "sample.txt" | ||
| 16 | (close-twice) close "sample.txt" again | ||
| 17 | close-twice: exit(-1) | ||
| 18 | EOF | ||
| 19 | pass; | ||
diff --git a/pintos-progos/tests/userprog/create-bad-ptr.c b/pintos-progos/tests/userprog/create-bad-ptr.c new file mode 100644 index 0000000..4a07bb3 --- /dev/null +++ b/pintos-progos/tests/userprog/create-bad-ptr.c | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | /* Passes a bad pointer to the create system call, | ||
| 2 | which must cause the process to be terminated with exit code | ||
| 3 | -1. */ | ||
| 4 | |||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | msg ("create(0x20101234): %d", create ((char *) 0x20101234, 0)); | ||
| 12 | } | ||
diff --git a/pintos-progos/tests/userprog/create-bad-ptr.ck b/pintos-progos/tests/userprog/create-bad-ptr.ck new file mode 100644 index 0000000..ac13405 --- /dev/null +++ b/pintos-progos/tests/userprog/create-bad-ptr.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (create-bad-ptr) begin | ||
| 7 | create-bad-ptr: exit(-1) | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/userprog/create-bound.c b/pintos-progos/tests/userprog/create-bound.c new file mode 100644 index 0000000..0a829f3 --- /dev/null +++ b/pintos-progos/tests/userprog/create-bound.c | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | /* Opens a file whose name spans the boundary between two pages. | ||
| 2 | This is valid, so it must succeed. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/userprog/boundary.h" | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | msg ("create(\"quux.dat\"): %d", | ||
| 13 | create (copy_string_across_boundary ("quux.dat"), 0)); | ||
| 14 | } | ||
diff --git a/pintos-progos/tests/userprog/create-bound.ck b/pintos-progos/tests/userprog/create-bound.ck new file mode 100644 index 0000000..7656b7f --- /dev/null +++ b/pintos-progos/tests/userprog/create-bound.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (create-bound) begin | ||
| 7 | (create-bound) create("quux.dat"): 1 | ||
| 8 | (create-bound) end | ||
| 9 | create-bound: exit(0) | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/userprog/create-empty.c b/pintos-progos/tests/userprog/create-empty.c new file mode 100644 index 0000000..fa26b43 --- /dev/null +++ b/pintos-progos/tests/userprog/create-empty.c | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | /* Tries to create a file with the empty string as its name. */ | ||
| 2 | |||
| 3 | #include "tests/lib.h" | ||
| 4 | #include "tests/main.h" | ||
| 5 | |||
| 6 | void | ||
| 7 | test_main (void) | ||
| 8 | { | ||
| 9 | msg ("create(\"\"): %d", create ("", 0)); | ||
| 10 | } | ||
diff --git a/pintos-progos/tests/userprog/create-empty.ck b/pintos-progos/tests/userprog/create-empty.ck new file mode 100644 index 0000000..93a1058 --- /dev/null +++ b/pintos-progos/tests/userprog/create-empty.ck | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (create-empty) begin | ||
| 7 | (create-empty) create(""): 0 | ||
| 8 | (create-empty) end | ||
| 9 | create-empty: exit(0) | ||
| 10 | EOF | ||
| 11 | (create-empty) begin | ||
| 12 | create-empty: exit(-1) | ||
| 13 | EOF | ||
| 14 | pass; | ||
diff --git a/pintos-progos/tests/userprog/create-exists.c b/pintos-progos/tests/userprog/create-exists.c new file mode 100644 index 0000000..d395008 --- /dev/null +++ b/pintos-progos/tests/userprog/create-exists.c | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | /* Verifies that trying to create a file under a name that | ||
| 2 | already exists will fail. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | CHECK (create ("quux.dat", 0), "create quux.dat"); | ||
| 12 | CHECK (create ("warble.dat", 0), "create warble.dat"); | ||
| 13 | CHECK (!create ("quux.dat", 0), "try to re-create quux.dat"); | ||
| 14 | CHECK (create ("baffle.dat", 0), "create baffle.dat"); | ||
| 15 | CHECK (!create ("warble.dat", 0), "try to re-create quux.dat"); | ||
| 16 | } | ||
diff --git a/pintos-progos/tests/userprog/create-exists.ck b/pintos-progos/tests/userprog/create-exists.ck new file mode 100644 index 0000000..006885e --- /dev/null +++ b/pintos-progos/tests/userprog/create-exists.ck | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (create-exists) begin | ||
| 7 | (create-exists) create quux.dat | ||
| 8 | (create-exists) create warble.dat | ||
| 9 | (create-exists) try to re-create quux.dat | ||
| 10 | (create-exists) create baffle.dat | ||
| 11 | (create-exists) try to re-create quux.dat | ||
| 12 | (create-exists) end | ||
| 13 | create-exists: exit(0) | ||
| 14 | EOF | ||
| 15 | pass; | ||
diff --git a/pintos-progos/tests/userprog/create-long.c b/pintos-progos/tests/userprog/create-long.c new file mode 100644 index 0000000..16b31bd --- /dev/null +++ b/pintos-progos/tests/userprog/create-long.c | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | /* Tries to create a file with a name that is much too long, | ||
| 2 | which must fail. */ | ||
| 3 | |||
| 4 | #include <string.h> | ||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | static char name[512]; | ||
| 13 | memset (name, 'x', sizeof name); | ||
| 14 | name[sizeof name - 1] = '\0'; | ||
| 15 | |||
| 16 | msg ("create(\"x...\"): %d", create (name, 0)); | ||
| 17 | } | ||
diff --git a/pintos-progos/tests/userprog/create-long.ck b/pintos-progos/tests/userprog/create-long.ck new file mode 100644 index 0000000..628411c --- /dev/null +++ b/pintos-progos/tests/userprog/create-long.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (create-long) begin | ||
| 7 | (create-long) create("x..."): 0 | ||
| 8 | (create-long) end | ||
| 9 | create-long: exit(0) | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/userprog/create-normal.c b/pintos-progos/tests/userprog/create-normal.c new file mode 100644 index 0000000..3cbc463 --- /dev/null +++ b/pintos-progos/tests/userprog/create-normal.c | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | /* Creates an ordinary empty file. */ | ||
| 2 | |||
| 3 | #include "tests/lib.h" | ||
| 4 | #include "tests/main.h" | ||
| 5 | |||
| 6 | void | ||
| 7 | test_main (void) | ||
| 8 | { | ||
| 9 | CHECK (create ("quux.dat", 0), "create quux.dat"); | ||
| 10 | } | ||
diff --git a/pintos-progos/tests/userprog/create-normal.ck b/pintos-progos/tests/userprog/create-normal.ck new file mode 100644 index 0000000..ca74a6e --- /dev/null +++ b/pintos-progos/tests/userprog/create-normal.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (create-normal) begin | ||
| 7 | (create-normal) create quux.dat | ||
| 8 | (create-normal) end | ||
| 9 | create-normal: exit(0) | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/userprog/create-null.c b/pintos-progos/tests/userprog/create-null.c new file mode 100644 index 0000000..287cb23 --- /dev/null +++ b/pintos-progos/tests/userprog/create-null.c | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | /* Tries to create a file with the null pointer as its name. | ||
| 2 | The process must be terminated with exit code -1. */ | ||
| 3 | |||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | msg ("create(NULL): %d", create (NULL, 0)); | ||
| 11 | } | ||
diff --git a/pintos-progos/tests/userprog/create-null.ck b/pintos-progos/tests/userprog/create-null.ck new file mode 100644 index 0000000..09b7872 --- /dev/null +++ b/pintos-progos/tests/userprog/create-null.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (create-null) begin | ||
| 7 | create-null: exit(-1) | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/userprog/exec-arg.c b/pintos-progos/tests/userprog/exec-arg.c new file mode 100644 index 0000000..82d0744 --- /dev/null +++ b/pintos-progos/tests/userprog/exec-arg.c | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | /* Tests argument passing to child processes. */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/main.h" | ||
| 5 | |||
| 6 | void | ||
| 7 | test_main (void) | ||
| 8 | { | ||
| 9 | wait (exec ("child-args childarg")); | ||
| 10 | } | ||
diff --git a/pintos-progos/tests/userprog/exec-arg.ck b/pintos-progos/tests/userprog/exec-arg.ck new file mode 100644 index 0000000..b7533ed --- /dev/null +++ b/pintos-progos/tests/userprog/exec-arg.ck | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (exec-arg) begin | ||
| 7 | (args) begin | ||
| 8 | (args) argc = 2 | ||
| 9 | (args) argv[0] = 'child-args' | ||
| 10 | (args) argv[1] = 'childarg' | ||
| 11 | (args) argv[2] = null | ||
| 12 | (args) end | ||
| 13 | child-args: exit(0) | ||
| 14 | (exec-arg) end | ||
| 15 | exec-arg: exit(0) | ||
| 16 | EOF | ||
| 17 | pass; | ||
diff --git a/pintos-progos/tests/userprog/exec-bad-ptr.c b/pintos-progos/tests/userprog/exec-bad-ptr.c new file mode 100644 index 0000000..0abadd3 --- /dev/null +++ b/pintos-progos/tests/userprog/exec-bad-ptr.c | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | /* Passes an invalid pointer to the exec system call. | ||
| 2 | The process must be terminated with -1 exit code. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | exec ((char *) 0x20101234); | ||
| 11 | } | ||
diff --git a/pintos-progos/tests/userprog/exec-bad-ptr.ck b/pintos-progos/tests/userprog/exec-bad-ptr.ck new file mode 100644 index 0000000..63f5f78 --- /dev/null +++ b/pintos-progos/tests/userprog/exec-bad-ptr.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (exec-bad-ptr) begin | ||
| 7 | (exec-bad-ptr) end | ||
| 8 | exec-bad-ptr: exit(0) | ||
| 9 | EOF | ||
| 10 | (exec-bad-ptr) begin | ||
| 11 | exec-bad-ptr: exit(-1) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/exec-missing.c b/pintos-progos/tests/userprog/exec-missing.c new file mode 100644 index 0000000..bf08cad --- /dev/null +++ b/pintos-progos/tests/userprog/exec-missing.c | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | /* Tries to execute a nonexistent process. | ||
| 2 | The exec system call must return -1. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | msg ("exec(\"no-such-file\"): %d", exec ("no-such-file")); | ||
| 12 | } | ||
diff --git a/pintos-progos/tests/userprog/exec-missing.ck b/pintos-progos/tests/userprog/exec-missing.ck new file mode 100644 index 0000000..0ef7aaa --- /dev/null +++ b/pintos-progos/tests/userprog/exec-missing.ck | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF', <<'EOF', <<'EOF']); | ||
| 6 | (exec-missing) begin | ||
| 7 | load: no-such-file: open failed | ||
| 8 | (exec-missing) exec("no-such-file"): -1 | ||
| 9 | (exec-missing) end | ||
| 10 | exec-missing: exit(0) | ||
| 11 | EOF | ||
| 12 | (exec-missing) begin | ||
| 13 | (exec-missing) exec("no-such-file"): -1 | ||
| 14 | (exec-missing) end | ||
| 15 | exec-missing: exit(0) | ||
| 16 | EOF | ||
| 17 | (exec-missing) begin | ||
| 18 | load: no-such-file: open failed | ||
| 19 | no-such-file: exit(-1) | ||
| 20 | (exec-missing) exec("no-such-file"): -1 | ||
| 21 | (exec-missing) end | ||
| 22 | exec-missing: exit(0) | ||
| 23 | EOF | ||
| 24 | (exec-missing) begin | ||
| 25 | load: no-such-file: open failed | ||
| 26 | (exec-missing) exec("no-such-file"): -1 | ||
| 27 | no-such-file: exit(-1) | ||
| 28 | (exec-missing) end | ||
| 29 | exec-missing: exit(0) | ||
| 30 | EOF | ||
| 31 | pass; | ||
diff --git a/pintos-progos/tests/userprog/exec-multiple.c b/pintos-progos/tests/userprog/exec-multiple.c new file mode 100644 index 0000000..ba4c26e --- /dev/null +++ b/pintos-progos/tests/userprog/exec-multiple.c | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | /* Executes and waits for multiple child processes. */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | wait (exec ("child-simple")); | ||
| 11 | wait (exec ("child-simple")); | ||
| 12 | wait (exec ("child-simple")); | ||
| 13 | wait (exec ("child-simple")); | ||
| 14 | } | ||
diff --git a/pintos-progos/tests/userprog/exec-multiple.ck b/pintos-progos/tests/userprog/exec-multiple.ck new file mode 100644 index 0000000..99624cd --- /dev/null +++ b/pintos-progos/tests/userprog/exec-multiple.ck | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (exec-multiple) begin | ||
| 7 | (child-simple) run | ||
| 8 | child-simple: exit(81) | ||
| 9 | (child-simple) run | ||
| 10 | child-simple: exit(81) | ||
| 11 | (child-simple) run | ||
| 12 | child-simple: exit(81) | ||
| 13 | (child-simple) run | ||
| 14 | child-simple: exit(81) | ||
| 15 | (exec-multiple) end | ||
| 16 | exec-multiple: exit(0) | ||
| 17 | EOF | ||
| 18 | pass; | ||
diff --git a/pintos-progos/tests/userprog/exec-once.c b/pintos-progos/tests/userprog/exec-once.c new file mode 100644 index 0000000..7bae5a1 --- /dev/null +++ b/pintos-progos/tests/userprog/exec-once.c | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | /* Executes and waits for a single child process. */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | wait (exec ("child-simple")); | ||
| 11 | } | ||
diff --git a/pintos-progos/tests/userprog/exec-once.ck b/pintos-progos/tests/userprog/exec-once.ck new file mode 100644 index 0000000..00b59ed --- /dev/null +++ b/pintos-progos/tests/userprog/exec-once.ck | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (exec-once) begin | ||
| 7 | (child-simple) run | ||
| 8 | child-simple: exit(81) | ||
| 9 | (exec-once) end | ||
| 10 | exec-once: exit(0) | ||
| 11 | EOF | ||
| 12 | pass; | ||
diff --git a/pintos-progos/tests/userprog/exit.c b/pintos-progos/tests/userprog/exit.c new file mode 100644 index 0000000..cb4eb8f --- /dev/null +++ b/pintos-progos/tests/userprog/exit.c | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | /* Tests the exit system call. */ | ||
| 2 | |||
| 3 | #include "tests/lib.h" | ||
| 4 | #include "tests/main.h" | ||
| 5 | |||
| 6 | void | ||
| 7 | test_main (void) | ||
| 8 | { | ||
| 9 | exit (57); | ||
| 10 | fail ("should have called exit(57)"); | ||
| 11 | } | ||
diff --git a/pintos-progos/tests/userprog/exit.ck b/pintos-progos/tests/userprog/exit.ck new file mode 100644 index 0000000..a552702 --- /dev/null +++ b/pintos-progos/tests/userprog/exit.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (exit) begin | ||
| 7 | exit: exit(57) | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/userprog/halt.c b/pintos-progos/tests/userprog/halt.c new file mode 100644 index 0000000..4a99bce --- /dev/null +++ b/pintos-progos/tests/userprog/halt.c | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | /* Tests the halt system call. */ | ||
| 2 | |||
| 3 | #include "tests/lib.h" | ||
| 4 | #include "tests/main.h" | ||
| 5 | |||
| 6 | void | ||
| 7 | test_main (void) | ||
| 8 | { | ||
| 9 | halt (); | ||
| 10 | fail ("should have halted"); | ||
| 11 | } | ||
diff --git a/pintos-progos/tests/userprog/halt.ck b/pintos-progos/tests/userprog/halt.ck new file mode 100644 index 0000000..1b701ed --- /dev/null +++ b/pintos-progos/tests/userprog/halt.ck | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | |||
| 6 | our ($test); | ||
| 7 | my (@output) = read_text_file ("$test.output"); | ||
| 8 | |||
| 9 | common_checks ("run", @output); | ||
| 10 | |||
| 11 | fail "missing 'begin' message\n" | ||
| 12 | if !grep ($_ eq '(halt) begin', @output); | ||
| 13 | fail "found 'fail' message--halt didn't really halt\n" | ||
| 14 | if grep ($_ eq '(halt) fail', @output); | ||
| 15 | pass; | ||
diff --git a/pintos-progos/tests/userprog/lib/.gitignore b/pintos-progos/tests/userprog/lib/.gitignore new file mode 100644 index 0000000..a438335 --- /dev/null +++ b/pintos-progos/tests/userprog/lib/.gitignore | |||
| @@ -0,0 +1 @@ | |||
| *.d | |||
diff --git a/pintos-progos/tests/userprog/lib/user/.dummy b/pintos-progos/tests/userprog/lib/user/.dummy new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/pintos-progos/tests/userprog/lib/user/.dummy | |||
diff --git a/pintos-progos/tests/userprog/lib/user/.gitignore b/pintos-progos/tests/userprog/lib/user/.gitignore new file mode 100644 index 0000000..a438335 --- /dev/null +++ b/pintos-progos/tests/userprog/lib/user/.gitignore | |||
| @@ -0,0 +1 @@ | |||
| *.d | |||
diff --git a/pintos-progos/tests/userprog/multi-child-fd.c b/pintos-progos/tests/userprog/multi-child-fd.c new file mode 100644 index 0000000..48de4b4 --- /dev/null +++ b/pintos-progos/tests/userprog/multi-child-fd.c | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | /* Opens a file and then runs a subprocess that tries to close | ||
| 2 | the file. (Pintos does not have inheritance of file handles, | ||
| 3 | so this must fail.) The parent process then attempts to use | ||
| 4 | the file handle, which must succeed. */ | ||
| 5 | |||
| 6 | #include <stdio.h> | ||
| 7 | #include <syscall.h> | ||
| 8 | #include "tests/userprog/sample.inc" | ||
| 9 | #include "tests/lib.h" | ||
| 10 | #include "tests/main.h" | ||
| 11 | |||
| 12 | void | ||
| 13 | test_main (void) | ||
| 14 | { | ||
| 15 | char child_cmd[128]; | ||
| 16 | int handle; | ||
| 17 | |||
| 18 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 19 | |||
| 20 | snprintf (child_cmd, sizeof child_cmd, "child-close %d", handle); | ||
| 21 | |||
| 22 | msg ("wait(exec()) = %d", wait (exec (child_cmd))); | ||
| 23 | |||
| 24 | check_file_handle (handle, "sample.txt", sample, sizeof sample - 1); | ||
| 25 | } | ||
diff --git a/pintos-progos/tests/userprog/multi-child-fd.ck b/pintos-progos/tests/userprog/multi-child-fd.ck new file mode 100644 index 0000000..d0b3a33 --- /dev/null +++ b/pintos-progos/tests/userprog/multi-child-fd.ck | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (multi-child-fd) begin | ||
| 7 | (multi-child-fd) open "sample.txt" | ||
| 8 | (child-close) begin | ||
| 9 | (child-close) end | ||
| 10 | child-close: exit(0) | ||
| 11 | (multi-child-fd) wait(exec()) = 0 | ||
| 12 | (multi-child-fd) verified contents of "sample.txt" | ||
| 13 | (multi-child-fd) end | ||
| 14 | multi-child-fd: exit(0) | ||
| 15 | EOF | ||
| 16 | (multi-child-fd) begin | ||
| 17 | (multi-child-fd) open "sample.txt" | ||
| 18 | (child-close) begin | ||
| 19 | child-close: exit(-1) | ||
| 20 | (multi-child-fd) wait(exec()) = -1 | ||
| 21 | (multi-child-fd) verified contents of "sample.txt" | ||
| 22 | (multi-child-fd) end | ||
| 23 | multi-child-fd: exit(0) | ||
| 24 | EOF | ||
| 25 | pass; | ||
diff --git a/pintos-progos/tests/userprog/multi-recurse.c b/pintos-progos/tests/userprog/multi-recurse.c new file mode 100644 index 0000000..7172ec3 --- /dev/null +++ b/pintos-progos/tests/userprog/multi-recurse.c | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | /* Executes itself recursively to the depth indicated by the | ||
| 2 | first command-line argument. */ | ||
| 3 | |||
| 4 | #include <debug.h> | ||
| 5 | #include <stdlib.h> | ||
| 6 | #include <stdio.h> | ||
| 7 | #include <syscall.h> | ||
| 8 | #include "tests/lib.h" | ||
| 9 | |||
| 10 | const char *test_name = "multi-recurse"; | ||
| 11 | |||
| 12 | int | ||
| 13 | main (int argc UNUSED, char *argv[]) | ||
| 14 | { | ||
| 15 | int n = atoi (argv[1]); | ||
| 16 | |||
| 17 | msg ("begin %d", n); | ||
| 18 | if (n != 0) | ||
| 19 | { | ||
| 20 | char child_cmd[128]; | ||
| 21 | pid_t child_pid; | ||
| 22 | int code; | ||
| 23 | |||
| 24 | snprintf (child_cmd, sizeof child_cmd, "multi-recurse %d", n - 1); | ||
| 25 | CHECK ((child_pid = exec (child_cmd)) != -1, "exec(\"%s\")", child_cmd); | ||
| 26 | |||
| 27 | code = wait (child_pid); | ||
| 28 | if (code != n - 1) | ||
| 29 | fail ("wait(exec(\"%s\")) returned %d", child_cmd, code); | ||
| 30 | } | ||
| 31 | |||
| 32 | msg ("end %d", n); | ||
| 33 | return n; | ||
| 34 | } | ||
diff --git a/pintos-progos/tests/userprog/multi-recurse.ck b/pintos-progos/tests/userprog/multi-recurse.ck new file mode 100644 index 0000000..41eb4a6 --- /dev/null +++ b/pintos-progos/tests/userprog/multi-recurse.ck | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (multi-recurse) begin 15 | ||
| 7 | (multi-recurse) exec("multi-recurse 14") | ||
| 8 | (multi-recurse) begin 14 | ||
| 9 | (multi-recurse) exec("multi-recurse 13") | ||
| 10 | (multi-recurse) begin 13 | ||
| 11 | (multi-recurse) exec("multi-recurse 12") | ||
| 12 | (multi-recurse) begin 12 | ||
| 13 | (multi-recurse) exec("multi-recurse 11") | ||
| 14 | (multi-recurse) begin 11 | ||
| 15 | (multi-recurse) exec("multi-recurse 10") | ||
| 16 | (multi-recurse) begin 10 | ||
| 17 | (multi-recurse) exec("multi-recurse 9") | ||
| 18 | (multi-recurse) begin 9 | ||
| 19 | (multi-recurse) exec("multi-recurse 8") | ||
| 20 | (multi-recurse) begin 8 | ||
| 21 | (multi-recurse) exec("multi-recurse 7") | ||
| 22 | (multi-recurse) begin 7 | ||
| 23 | (multi-recurse) exec("multi-recurse 6") | ||
| 24 | (multi-recurse) begin 6 | ||
| 25 | (multi-recurse) exec("multi-recurse 5") | ||
| 26 | (multi-recurse) begin 5 | ||
| 27 | (multi-recurse) exec("multi-recurse 4") | ||
| 28 | (multi-recurse) begin 4 | ||
| 29 | (multi-recurse) exec("multi-recurse 3") | ||
| 30 | (multi-recurse) begin 3 | ||
| 31 | (multi-recurse) exec("multi-recurse 2") | ||
| 32 | (multi-recurse) begin 2 | ||
| 33 | (multi-recurse) exec("multi-recurse 1") | ||
| 34 | (multi-recurse) begin 1 | ||
| 35 | (multi-recurse) exec("multi-recurse 0") | ||
| 36 | (multi-recurse) begin 0 | ||
| 37 | (multi-recurse) end 0 | ||
| 38 | multi-recurse: exit(0) | ||
| 39 | (multi-recurse) end 1 | ||
| 40 | multi-recurse: exit(1) | ||
| 41 | (multi-recurse) end 2 | ||
| 42 | multi-recurse: exit(2) | ||
| 43 | (multi-recurse) end 3 | ||
| 44 | multi-recurse: exit(3) | ||
| 45 | (multi-recurse) end 4 | ||
| 46 | multi-recurse: exit(4) | ||
| 47 | (multi-recurse) end 5 | ||
| 48 | multi-recurse: exit(5) | ||
| 49 | (multi-recurse) end 6 | ||
| 50 | multi-recurse: exit(6) | ||
| 51 | (multi-recurse) end 7 | ||
| 52 | multi-recurse: exit(7) | ||
| 53 | (multi-recurse) end 8 | ||
| 54 | multi-recurse: exit(8) | ||
| 55 | (multi-recurse) end 9 | ||
| 56 | multi-recurse: exit(9) | ||
| 57 | (multi-recurse) end 10 | ||
| 58 | multi-recurse: exit(10) | ||
| 59 | (multi-recurse) end 11 | ||
| 60 | multi-recurse: exit(11) | ||
| 61 | (multi-recurse) end 12 | ||
| 62 | multi-recurse: exit(12) | ||
| 63 | (multi-recurse) end 13 | ||
| 64 | multi-recurse: exit(13) | ||
| 65 | (multi-recurse) end 14 | ||
| 66 | multi-recurse: exit(14) | ||
| 67 | (multi-recurse) end 15 | ||
| 68 | multi-recurse: exit(15) | ||
| 69 | EOF | ||
| 70 | pass; | ||
diff --git a/pintos-progos/tests/userprog/no-vm/Make.tests b/pintos-progos/tests/userprog/no-vm/Make.tests new file mode 100644 index 0000000..a545e18 --- /dev/null +++ b/pintos-progos/tests/userprog/no-vm/Make.tests | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | |||
| 3 | tests/userprog/no-vm_TESTS = tests/userprog/no-vm/multi-oom | ||
| 4 | tests/userprog/no-vm_PROGS = $(tests/userprog/no-vm_TESTS) | ||
| 5 | tests/userprog/no-vm/multi-oom_SRC = tests/userprog/no-vm/multi-oom.c \ | ||
| 6 | tests/lib.c | ||
| 7 | |||
| 8 | tests/userprog/no-vm/multi-oom.output: TIMEOUT = 360 | ||
diff --git a/pintos-progos/tests/userprog/no-vm/Rubric b/pintos-progos/tests/userprog/no-vm/Rubric new file mode 100644 index 0000000..c3816c6 --- /dev/null +++ b/pintos-progos/tests/userprog/no-vm/Rubric | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | Functionality of features that VM might break: | ||
| 2 | |||
| 3 | 1 multi-oom | ||
diff --git a/pintos-progos/tests/userprog/no-vm/multi-oom.c b/pintos-progos/tests/userprog/no-vm/multi-oom.c new file mode 100644 index 0000000..6a4472d --- /dev/null +++ b/pintos-progos/tests/userprog/no-vm/multi-oom.c | |||
| @@ -0,0 +1,179 @@ | |||
| 1 | /* Recursively executes itself until the child fails to execute. | ||
| 2 | We expect that at least 30 copies can run. | ||
| 3 | |||
| 4 | We count how many children your kernel was able to execute | ||
| 5 | before it fails to start a new process. We require that, | ||
| 6 | if a process doesn't actually get to start, exec() must | ||
| 7 | return -1, not a valid PID. | ||
| 8 | |||
| 9 | We repeat this process 10 times, checking that your kernel | ||
| 10 | allows for the same level of depth every time. | ||
| 11 | |||
| 12 | In addition, some processes will spawn children that terminate | ||
| 13 | abnormally after allocating some resources. | ||
| 14 | |||
| 15 | Written by Godmar Back <godmar@gmail.com> | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <debug.h> | ||
| 19 | #include <stdio.h> | ||
| 20 | #include <string.h> | ||
| 21 | #include <stdlib.h> | ||
| 22 | #include <stdbool.h> | ||
| 23 | #include <syscall.h> | ||
| 24 | #include <random.h> | ||
| 25 | #include "tests/lib.h" | ||
| 26 | |||
| 27 | static const int EXPECTED_DEPTH_TO_PASS = 30; | ||
| 28 | static const int EXPECTED_REPETITIONS = 10; | ||
| 29 | |||
| 30 | const char *test_name = "multi-oom"; | ||
| 31 | |||
| 32 | enum child_termination_mode { RECURSE, CRASH }; | ||
| 33 | |||
| 34 | /* Spawn a recursive copy of ourselves, passing along instructions | ||
| 35 | for the child. */ | ||
| 36 | static pid_t | ||
| 37 | spawn_child (int c, enum child_termination_mode mode) | ||
| 38 | { | ||
| 39 | char child_cmd[128]; | ||
| 40 | snprintf (child_cmd, sizeof child_cmd, | ||
| 41 | "%s %d %s", test_name, c, mode == CRASH ? "-k" : ""); | ||
| 42 | return exec (child_cmd); | ||
| 43 | } | ||
| 44 | |||
| 45 | /* Open a number of files (and fail to close them). | ||
| 46 | The kernel must free any kernel resources associated | ||
| 47 | with these file descriptors. */ | ||
| 48 | static void | ||
| 49 | consume_some_resources (void) | ||
| 50 | { | ||
| 51 | int fd, fdmax = 126; | ||
| 52 | |||
| 53 | /* Open as many files as we can, up to fdmax. | ||
| 54 | Depending on how file descriptors are allocated inside | ||
| 55 | the kernel, open() may fail if the kernel is low on memory. | ||
| 56 | A low-memory condition in open() should not lead to the | ||
| 57 | termination of the process. */ | ||
| 58 | for (fd = 0; fd < fdmax; fd++) | ||
| 59 | if (open (test_name) == -1) | ||
| 60 | break; | ||
| 61 | } | ||
| 62 | |||
| 63 | /* Consume some resources, then terminate this process | ||
| 64 | in some abnormal way. */ | ||
| 65 | static int NO_INLINE | ||
| 66 | consume_some_resources_and_die (int seed) | ||
| 67 | { | ||
| 68 | consume_some_resources (); | ||
| 69 | random_init (seed); | ||
| 70 | int *PHYS_BASE = (int *)0xC0000000; | ||
| 71 | |||
| 72 | switch (random_ulong () % 5) | ||
| 73 | { | ||
| 74 | case 0: | ||
| 75 | *(int *) NULL = 42; | ||
| 76 | |||
| 77 | case 1: | ||
| 78 | return *(int *) NULL; | ||
| 79 | |||
| 80 | case 2: | ||
| 81 | return *PHYS_BASE; | ||
| 82 | |||
| 83 | case 3: | ||
| 84 | *PHYS_BASE = 42; | ||
| 85 | |||
| 86 | case 4: | ||
| 87 | open ((char *)PHYS_BASE); | ||
| 88 | exit (-1); | ||
| 89 | |||
| 90 | default: | ||
| 91 | NOT_REACHED (); | ||
| 92 | } | ||
| 93 | return 0; | ||
| 94 | } | ||
| 95 | |||
| 96 | /* The first copy is invoked without command line arguments. | ||
| 97 | Subsequent copies are invoked with a parameter 'depth' | ||
| 98 | that describes how many parent processes preceded them. | ||
| 99 | Each process spawns one or multiple recursive copies of | ||
| 100 | itself, passing 'depth+1' as depth. | ||
| 101 | |||
| 102 | Some children are started with the '-k' flag, which will | ||
| 103 | result in abnormal termination. | ||
| 104 | */ | ||
| 105 | int | ||
| 106 | main (int argc, char *argv[]) | ||
| 107 | { | ||
| 108 | int n; | ||
| 109 | |||
| 110 | n = argc > 1 ? atoi (argv[1]) : 0; | ||
| 111 | bool is_at_root = (n == 0); | ||
| 112 | if (is_at_root) | ||
| 113 | msg ("begin"); | ||
| 114 | |||
| 115 | /* If -k is passed, crash this process. */ | ||
| 116 | if (argc > 2 && !strcmp(argv[2], "-k")) | ||
| 117 | { | ||
| 118 | consume_some_resources_and_die (n); | ||
| 119 | NOT_REACHED (); | ||
| 120 | } | ||
| 121 | |||
| 122 | int howmany = is_at_root ? EXPECTED_REPETITIONS : 1; | ||
| 123 | int i, expected_depth = -1; | ||
| 124 | |||
| 125 | for (i = 0; i < howmany; i++) | ||
| 126 | { | ||
| 127 | pid_t child_pid; | ||
| 128 | |||
| 129 | /* Spawn a child that will be abnormally terminated. | ||
| 130 | To speed the test up, do this only for processes | ||
| 131 | spawned at a certain depth. */ | ||
| 132 | if (n > EXPECTED_DEPTH_TO_PASS/2) | ||
| 133 | { | ||
| 134 | child_pid = spawn_child (n + 1, CRASH); | ||
| 135 | if (child_pid != -1) | ||
| 136 | { | ||
| 137 | if (wait (child_pid) != -1) | ||
| 138 | fail ("crashed child should return -1."); | ||
| 139 | } | ||
| 140 | /* If spawning this child failed, so should | ||
| 141 | the next spawn_child below. */ | ||
| 142 | } | ||
| 143 | |||
| 144 | /* Now spawn the child that will recurse. */ | ||
| 145 | child_pid = spawn_child (n + 1, RECURSE); | ||
| 146 | |||
| 147 | /* If maximum depth is reached, return result. */ | ||
| 148 | if (child_pid == -1) | ||
| 149 | return n; | ||
| 150 | |||
| 151 | /* Else wait for child to report how deeply it was able to recurse. */ | ||
| 152 | int reached_depth = wait (child_pid); | ||
| 153 | if (reached_depth == -1) | ||
| 154 | fail ("wait returned -1."); | ||
| 155 | |||
| 156 | /* Record the depth reached during the first run; on subsequent | ||
| 157 | runs, fail if those runs do not match the depth achieved on the | ||
| 158 | first run. */ | ||
| 159 | if (i == 0) | ||
| 160 | expected_depth = reached_depth; | ||
| 161 | else if (expected_depth != reached_depth) | ||
| 162 | fail ("after run %d/%d, expected depth %d, actual depth %d.", | ||
| 163 | i, howmany, expected_depth, reached_depth); | ||
| 164 | ASSERT (expected_depth == reached_depth); | ||
| 165 | } | ||
| 166 | |||
| 167 | consume_some_resources (); | ||
| 168 | |||
| 169 | if (n == 0) | ||
| 170 | { | ||
| 171 | if (expected_depth < EXPECTED_DEPTH_TO_PASS) | ||
| 172 | fail ("should have forked at least %d times.", EXPECTED_DEPTH_TO_PASS); | ||
| 173 | msg ("success. program forked %d times.", howmany); | ||
| 174 | msg ("end"); | ||
| 175 | } | ||
| 176 | |||
| 177 | return expected_depth; | ||
| 178 | } | ||
| 179 | // vim: sw=2 | ||
diff --git a/pintos-progos/tests/userprog/no-vm/multi-oom.ck b/pintos-progos/tests/userprog/no-vm/multi-oom.ck new file mode 100644 index 0000000..59a0bcd --- /dev/null +++ b/pintos-progos/tests/userprog/no-vm/multi-oom.ck | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_USER_FAULTS => 1, IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (multi-oom) begin | ||
| 7 | (multi-oom) success. program forked 10 times. | ||
| 8 | (multi-oom) end | ||
| 9 | EOF | ||
| 10 | pass; | ||
diff --git a/pintos-progos/tests/userprog/null.ck b/pintos-progos/tests/userprog/null.ck new file mode 100644 index 0000000..980de35 --- /dev/null +++ b/pintos-progos/tests/userprog/null.ck | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | system call! | ||
| 7 | EOF | ||
| 8 | pass; | ||
diff --git a/pintos-progos/tests/userprog/open-bad-ptr.c b/pintos-progos/tests/userprog/open-bad-ptr.c new file mode 100644 index 0000000..9cd4edf --- /dev/null +++ b/pintos-progos/tests/userprog/open-bad-ptr.c | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | /* Passes an invalid pointer to the open system call. | ||
| 2 | The process must be terminated with -1 exit code. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | msg ("open(0x20101234): %d", open ((char *) 0x20101234)); | ||
| 12 | fail ("should have called exit(-1)"); | ||
| 13 | } | ||
diff --git a/pintos-progos/tests/userprog/open-bad-ptr.ck b/pintos-progos/tests/userprog/open-bad-ptr.ck new file mode 100644 index 0000000..45349e2 --- /dev/null +++ b/pintos-progos/tests/userprog/open-bad-ptr.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (open-bad-ptr) begin | ||
| 7 | (open-bad-ptr) end | ||
| 8 | open-bad-ptr: exit(0) | ||
| 9 | EOF | ||
| 10 | (open-bad-ptr) begin | ||
| 11 | open-bad-ptr: exit(-1) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/open-boundary.c b/pintos-progos/tests/userprog/open-boundary.c new file mode 100644 index 0000000..cc8ff8b --- /dev/null +++ b/pintos-progos/tests/userprog/open-boundary.c | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | /* Creates a file whose name spans the boundary between two pages. | ||
| 2 | This is valid, so it must succeed. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/userprog/boundary.h" | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | CHECK (open (copy_string_across_boundary ("sample.txt")) > 1, | ||
| 13 | "open \"sample.txt\""); | ||
| 14 | } | ||
diff --git a/pintos-progos/tests/userprog/open-boundary.ck b/pintos-progos/tests/userprog/open-boundary.ck new file mode 100644 index 0000000..8060d22 --- /dev/null +++ b/pintos-progos/tests/userprog/open-boundary.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (open-boundary) begin | ||
| 7 | (open-boundary) open "sample.txt" | ||
| 8 | (open-boundary) end | ||
| 9 | open-boundary: exit(0) | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/userprog/open-empty.c b/pintos-progos/tests/userprog/open-empty.c new file mode 100644 index 0000000..3ea9907 --- /dev/null +++ b/pintos-progos/tests/userprog/open-empty.c | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | /* Tries to open a file with the empty string as its name. */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | int handle = open (""); | ||
| 11 | if (handle != -1) | ||
| 12 | fail ("open() returned %d instead of -1", handle); | ||
| 13 | } | ||
diff --git a/pintos-progos/tests/userprog/open-empty.ck b/pintos-progos/tests/userprog/open-empty.ck new file mode 100644 index 0000000..885fb41 --- /dev/null +++ b/pintos-progos/tests/userprog/open-empty.ck | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (open-empty) begin | ||
| 7 | (open-empty) end | ||
| 8 | open-empty: exit(0) | ||
| 9 | EOF | ||
| 10 | pass; | ||
diff --git a/pintos-progos/tests/userprog/open-missing.c b/pintos-progos/tests/userprog/open-missing.c new file mode 100644 index 0000000..13ecbda --- /dev/null +++ b/pintos-progos/tests/userprog/open-missing.c | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | /* Tries to open a nonexistent file. */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | int handle = open ("no-such-file"); | ||
| 11 | if (handle != -1) | ||
| 12 | fail ("open() returned %d", handle); | ||
| 13 | } | ||
diff --git a/pintos-progos/tests/userprog/open-missing.ck b/pintos-progos/tests/userprog/open-missing.ck new file mode 100644 index 0000000..d72d878 --- /dev/null +++ b/pintos-progos/tests/userprog/open-missing.ck | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (open-missing) begin | ||
| 7 | (open-missing) end | ||
| 8 | open-missing: exit(0) | ||
| 9 | EOF | ||
| 10 | pass; | ||
diff --git a/pintos-progos/tests/userprog/open-normal.c b/pintos-progos/tests/userprog/open-normal.c new file mode 100644 index 0000000..5132465 --- /dev/null +++ b/pintos-progos/tests/userprog/open-normal.c | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | /* Open a file. */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | int handle = open ("sample.txt"); | ||
| 11 | if (handle < 2) | ||
| 12 | fail ("open() returned %d", handle); | ||
| 13 | } | ||
diff --git a/pintos-progos/tests/userprog/open-normal.ck b/pintos-progos/tests/userprog/open-normal.ck new file mode 100644 index 0000000..4f6c342 --- /dev/null +++ b/pintos-progos/tests/userprog/open-normal.ck | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (open-normal) begin | ||
| 7 | (open-normal) end | ||
| 8 | open-normal: exit(0) | ||
| 9 | EOF | ||
| 10 | pass; | ||
diff --git a/pintos-progos/tests/userprog/open-null.c b/pintos-progos/tests/userprog/open-null.c new file mode 100644 index 0000000..bb418b8 --- /dev/null +++ b/pintos-progos/tests/userprog/open-null.c | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | /* Tries to open a file with the null pointer as its name. | ||
| 2 | The process must be terminated with exit code -1. */ | ||
| 3 | |||
| 4 | #include <stddef.h> | ||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | open (NULL); | ||
| 12 | } | ||
diff --git a/pintos-progos/tests/userprog/open-null.ck b/pintos-progos/tests/userprog/open-null.ck new file mode 100644 index 0000000..b4a3bcb --- /dev/null +++ b/pintos-progos/tests/userprog/open-null.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (open-null) begin | ||
| 7 | (open-null) end | ||
| 8 | open-null: exit(0) | ||
| 9 | EOF | ||
| 10 | (open-null) begin | ||
| 11 | open-null: exit(-1) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/open-twice.c b/pintos-progos/tests/userprog/open-twice.c new file mode 100644 index 0000000..dd333af --- /dev/null +++ b/pintos-progos/tests/userprog/open-twice.c | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | /* Tries to open the same file twice, | ||
| 2 | which must succeed and must return a different file descriptor | ||
| 3 | in each case. */ | ||
| 4 | |||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | int h1 = open ("sample.txt"); | ||
| 13 | int h2 = open ("sample.txt"); | ||
| 14 | |||
| 15 | CHECK ((h1 = open ("sample.txt")) > 1, "open \"sample.txt\" once"); | ||
| 16 | CHECK ((h2 = open ("sample.txt")) > 1, "open \"sample.txt\" again"); | ||
| 17 | if (h1 == h2) | ||
| 18 | fail ("open() returned %d both times", h1); | ||
| 19 | } | ||
diff --git a/pintos-progos/tests/userprog/open-twice.ck b/pintos-progos/tests/userprog/open-twice.ck new file mode 100644 index 0000000..64fa805 --- /dev/null +++ b/pintos-progos/tests/userprog/open-twice.ck | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (open-twice) begin | ||
| 7 | (open-twice) open "sample.txt" once | ||
| 8 | (open-twice) open "sample.txt" again | ||
| 9 | (open-twice) end | ||
| 10 | open-twice: exit(0) | ||
| 11 | EOF | ||
| 12 | pass; | ||
diff --git a/pintos-progos/tests/userprog/read-bad-fd.c b/pintos-progos/tests/userprog/read-bad-fd.c new file mode 100644 index 0000000..a8b190d --- /dev/null +++ b/pintos-progos/tests/userprog/read-bad-fd.c | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | /* Tries to read from an invalid fd, | ||
| 2 | which must either fail silently or terminate the process with | ||
| 3 | exit code -1. */ | ||
| 4 | |||
| 5 | #include <limits.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | void | ||
| 11 | test_main (void) | ||
| 12 | { | ||
| 13 | char buf; | ||
| 14 | read (0x20101234, &buf, 1); | ||
| 15 | read (5, &buf, 1); | ||
| 16 | read (1234, &buf, 1); | ||
| 17 | read (-1, &buf, 1); | ||
| 18 | read (-1024, &buf, 1); | ||
| 19 | read (INT_MIN, &buf, 1); | ||
| 20 | read (INT_MAX, &buf, 1); | ||
| 21 | } | ||
diff --git a/pintos-progos/tests/userprog/read-bad-fd.ck b/pintos-progos/tests/userprog/read-bad-fd.ck new file mode 100644 index 0000000..5fedcc7 --- /dev/null +++ b/pintos-progos/tests/userprog/read-bad-fd.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (read-bad-fd) begin | ||
| 7 | (read-bad-fd) end | ||
| 8 | read-bad-fd: exit(0) | ||
| 9 | EOF | ||
| 10 | (read-bad-fd) begin | ||
| 11 | read-bad-fd: exit(-1) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/read-bad-ptr.c b/pintos-progos/tests/userprog/read-bad-ptr.c new file mode 100644 index 0000000..8fe756e --- /dev/null +++ b/pintos-progos/tests/userprog/read-bad-ptr.c | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | /* Passes an invalid pointer to the read system call. | ||
| 2 | The process must be terminated with -1 exit code. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | int handle; | ||
| 12 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 13 | |||
| 14 | read (handle, (char *) 0xc0100000, 123); | ||
| 15 | fail ("should not have survived read()"); | ||
| 16 | } | ||
diff --git a/pintos-progos/tests/userprog/read-bad-ptr.ck b/pintos-progos/tests/userprog/read-bad-ptr.ck new file mode 100644 index 0000000..d10accf --- /dev/null +++ b/pintos-progos/tests/userprog/read-bad-ptr.ck | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (read-bad-ptr) begin | ||
| 7 | (read-bad-ptr) open "sample.txt" | ||
| 8 | (read-bad-ptr) end | ||
| 9 | read-bad-ptr: exit(0) | ||
| 10 | EOF | ||
| 11 | (read-bad-ptr) begin | ||
| 12 | (read-bad-ptr) open "sample.txt" | ||
| 13 | read-bad-ptr: exit(-1) | ||
| 14 | EOF | ||
| 15 | pass; | ||
diff --git a/pintos-progos/tests/userprog/read-boundary.c b/pintos-progos/tests/userprog/read-boundary.c new file mode 100644 index 0000000..9c19966 --- /dev/null +++ b/pintos-progos/tests/userprog/read-boundary.c | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | /* Reads data spanning two pages in virtual address space, | ||
| 2 | which must succeed. */ | ||
| 3 | |||
| 4 | #include <string.h> | ||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/userprog/boundary.h" | ||
| 7 | #include "tests/userprog/sample.inc" | ||
| 8 | #include "tests/lib.h" | ||
| 9 | #include "tests/main.h" | ||
| 10 | |||
| 11 | void | ||
| 12 | test_main (void) | ||
| 13 | { | ||
| 14 | int handle; | ||
| 15 | int byte_cnt; | ||
| 16 | char *buffer; | ||
| 17 | |||
| 18 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 19 | |||
| 20 | buffer = get_boundary_area () - sizeof sample / 2; | ||
| 21 | byte_cnt = read (handle, buffer, sizeof sample - 1); | ||
| 22 | if (byte_cnt != sizeof sample - 1) | ||
| 23 | fail ("read() returned %d instead of %zu", byte_cnt, sizeof sample - 1); | ||
| 24 | else if (strcmp (sample, buffer)) | ||
| 25 | { | ||
| 26 | msg ("expected text:\n%s", sample); | ||
| 27 | msg ("text actually read:\n%s", buffer); | ||
| 28 | fail ("expected text differs from actual"); | ||
| 29 | } | ||
| 30 | } | ||
diff --git a/pintos-progos/tests/userprog/read-boundary.ck b/pintos-progos/tests/userprog/read-boundary.ck new file mode 100644 index 0000000..08dc161 --- /dev/null +++ b/pintos-progos/tests/userprog/read-boundary.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (read-boundary) begin | ||
| 7 | (read-boundary) open "sample.txt" | ||
| 8 | (read-boundary) end | ||
| 9 | read-boundary: exit(0) | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/userprog/read-normal.c b/pintos-progos/tests/userprog/read-normal.c new file mode 100644 index 0000000..16d15cc --- /dev/null +++ b/pintos-progos/tests/userprog/read-normal.c | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | /* Try reading a file in the most normal way. */ | ||
| 2 | |||
| 3 | #include "tests/userprog/sample.inc" | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | check_file ("sample.txt", sample, sizeof sample - 1); | ||
| 11 | } | ||
diff --git a/pintos-progos/tests/userprog/read-normal.ck b/pintos-progos/tests/userprog/read-normal.ck new file mode 100644 index 0000000..0ed2998 --- /dev/null +++ b/pintos-progos/tests/userprog/read-normal.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (read-normal) begin | ||
| 7 | (read-normal) open "sample.txt" for verification | ||
| 8 | (read-normal) verified contents of "sample.txt" | ||
| 9 | (read-normal) close "sample.txt" | ||
| 10 | (read-normal) end | ||
| 11 | read-normal: exit(0) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/read-stdout.c b/pintos-progos/tests/userprog/read-stdout.c new file mode 100644 index 0000000..d0630b9 --- /dev/null +++ b/pintos-progos/tests/userprog/read-stdout.c | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | /* Try reading from fd 1 (stdout), | ||
| 2 | which may just fail or terminate the process with -1 exit | ||
| 3 | code. */ | ||
| 4 | |||
| 5 | #include <stdio.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | char buf; | ||
| 13 | read (STDOUT_FILENO, &buf, 1); | ||
| 14 | } | ||
diff --git a/pintos-progos/tests/userprog/read-stdout.ck b/pintos-progos/tests/userprog/read-stdout.ck new file mode 100644 index 0000000..7d87b52 --- /dev/null +++ b/pintos-progos/tests/userprog/read-stdout.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (read-stdout) begin | ||
| 7 | (read-stdout) end | ||
| 8 | read-stdout: exit(0) | ||
| 9 | EOF | ||
| 10 | (read-stdout) begin | ||
| 11 | read-stdout: exit(-1) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/read-zero.c b/pintos-progos/tests/userprog/read-zero.c new file mode 100644 index 0000000..e441817 --- /dev/null +++ b/pintos-progos/tests/userprog/read-zero.c | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | /* Try a 0-byte read, which should return 0 without reading | ||
| 2 | anything. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | int handle, byte_cnt; | ||
| 12 | char buf; | ||
| 13 | |||
| 14 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 15 | |||
| 16 | buf = 123; | ||
| 17 | byte_cnt = read (handle, &buf, 0); | ||
| 18 | if (byte_cnt != 0) | ||
| 19 | fail ("read() returned %d instead of 0", byte_cnt); | ||
| 20 | else if (buf != 123) | ||
| 21 | fail ("0-byte read() modified buffer"); | ||
| 22 | } | ||
diff --git a/pintos-progos/tests/userprog/read-zero.ck b/pintos-progos/tests/userprog/read-zero.ck new file mode 100644 index 0000000..8346dbc --- /dev/null +++ b/pintos-progos/tests/userprog/read-zero.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (read-zero) begin | ||
| 7 | (read-zero) open "sample.txt" | ||
| 8 | (read-zero) end | ||
| 9 | read-zero: exit(0) | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/userprog/rox-child.c b/pintos-progos/tests/userprog/rox-child.c new file mode 100644 index 0000000..30afba2 --- /dev/null +++ b/pintos-progos/tests/userprog/rox-child.c | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | /* Ensure that the executable of a running process cannot be | ||
| 2 | modified, even by a child process. */ | ||
| 3 | |||
| 4 | #define CHILD_CNT "1" | ||
| 5 | #include "tests/userprog/rox-child.inc" | ||
diff --git a/pintos-progos/tests/userprog/rox-child.ck b/pintos-progos/tests/userprog/rox-child.ck new file mode 100644 index 0000000..e6363fb --- /dev/null +++ b/pintos-progos/tests/userprog/rox-child.ck | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (rox-child) begin | ||
| 7 | (rox-child) open "child-rox" | ||
| 8 | (rox-child) read "child-rox" | ||
| 9 | (rox-child) write "child-rox" | ||
| 10 | (rox-child) exec "child-rox 1" | ||
| 11 | (child-rox) begin | ||
| 12 | (child-rox) try to write "child-rox" | ||
| 13 | (child-rox) try to write "child-rox" | ||
| 14 | (child-rox) end | ||
| 15 | child-rox: exit(12) | ||
| 16 | (rox-child) write "child-rox" | ||
| 17 | (rox-child) end | ||
| 18 | rox-child: exit(0) | ||
| 19 | EOF | ||
| 20 | pass; | ||
diff --git a/pintos-progos/tests/userprog/rox-child.inc b/pintos-progos/tests/userprog/rox-child.inc new file mode 100644 index 0000000..1e2ade9 --- /dev/null +++ b/pintos-progos/tests/userprog/rox-child.inc | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | /* -*- c -*- */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | const char *child_cmd = "child-rox " CHILD_CNT; | ||
| 11 | int handle; | ||
| 12 | pid_t child; | ||
| 13 | char buffer[16]; | ||
| 14 | |||
| 15 | /* Open child-rox, read from it, write back same data. */ | ||
| 16 | CHECK ((handle = open ("child-rox")) > 1, "open \"child-rox\""); | ||
| 17 | CHECK (read (handle, buffer, sizeof buffer) == (int) sizeof buffer, | ||
| 18 | "read \"child-rox\""); | ||
| 19 | seek (handle, 0); | ||
| 20 | CHECK (write (handle, buffer, sizeof buffer) == (int) sizeof buffer, | ||
| 21 | "write \"child-rox\""); | ||
| 22 | |||
| 23 | /* Execute child-rox and wait for it. */ | ||
| 24 | CHECK ((child = exec (child_cmd)) != -1, "exec \"%s\"", child_cmd); | ||
| 25 | quiet = true; | ||
| 26 | CHECK (wait (child) == 12, "wait for child"); | ||
| 27 | quiet = false; | ||
| 28 | |||
| 29 | /* Write to child-rox again. */ | ||
| 30 | seek (handle, 0); | ||
| 31 | CHECK (write (handle, buffer, sizeof buffer) == (int) sizeof buffer, | ||
| 32 | "write \"child-rox\""); | ||
| 33 | } | ||
diff --git a/pintos-progos/tests/userprog/rox-multichild.c b/pintos-progos/tests/userprog/rox-multichild.c new file mode 100644 index 0000000..8e74dab --- /dev/null +++ b/pintos-progos/tests/userprog/rox-multichild.c | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | /* Ensure that the executable of a running process cannot be | ||
| 2 | modified, even in the presence of multiple children. */ | ||
| 3 | |||
| 4 | #define CHILD_CNT "5" | ||
| 5 | #include "tests/userprog/rox-child.inc" | ||
diff --git a/pintos-progos/tests/userprog/rox-multichild.ck b/pintos-progos/tests/userprog/rox-multichild.ck new file mode 100644 index 0000000..14b27db --- /dev/null +++ b/pintos-progos/tests/userprog/rox-multichild.ck | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (rox-multichild) begin | ||
| 7 | (rox-multichild) open "child-rox" | ||
| 8 | (rox-multichild) read "child-rox" | ||
| 9 | (rox-multichild) write "child-rox" | ||
| 10 | (rox-multichild) exec "child-rox 5" | ||
| 11 | (child-rox) begin | ||
| 12 | (child-rox) try to write "child-rox" | ||
| 13 | (child-rox) exec "child-rox 4" | ||
| 14 | (child-rox) begin | ||
| 15 | (child-rox) try to write "child-rox" | ||
| 16 | (child-rox) exec "child-rox 3" | ||
| 17 | (child-rox) begin | ||
| 18 | (child-rox) try to write "child-rox" | ||
| 19 | (child-rox) exec "child-rox 2" | ||
| 20 | (child-rox) begin | ||
| 21 | (child-rox) try to write "child-rox" | ||
| 22 | (child-rox) exec "child-rox 1" | ||
| 23 | (child-rox) begin | ||
| 24 | (child-rox) try to write "child-rox" | ||
| 25 | (child-rox) try to write "child-rox" | ||
| 26 | (child-rox) end | ||
| 27 | child-rox: exit(12) | ||
| 28 | (child-rox) try to write "child-rox" | ||
| 29 | (child-rox) end | ||
| 30 | child-rox: exit(12) | ||
| 31 | (child-rox) try to write "child-rox" | ||
| 32 | (child-rox) end | ||
| 33 | child-rox: exit(12) | ||
| 34 | (child-rox) try to write "child-rox" | ||
| 35 | (child-rox) end | ||
| 36 | child-rox: exit(12) | ||
| 37 | (child-rox) try to write "child-rox" | ||
| 38 | (child-rox) end | ||
| 39 | child-rox: exit(12) | ||
| 40 | (rox-multichild) write "child-rox" | ||
| 41 | (rox-multichild) end | ||
| 42 | rox-multichild: exit(0) | ||
| 43 | EOF | ||
| 44 | pass; | ||
diff --git a/pintos-progos/tests/userprog/rox-simple.c b/pintos-progos/tests/userprog/rox-simple.c new file mode 100644 index 0000000..e84a064 --- /dev/null +++ b/pintos-progos/tests/userprog/rox-simple.c | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | /* Ensure that the executable of a running process cannot be | ||
| 2 | modified. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | int handle; | ||
| 12 | char buffer[16]; | ||
| 13 | |||
| 14 | CHECK ((handle = open ("rox-simple")) > 1, "open \"rox-simple\""); | ||
| 15 | CHECK (read (handle, buffer, sizeof buffer) == (int) sizeof buffer, | ||
| 16 | "read \"rox-simple\""); | ||
| 17 | CHECK (write (handle, buffer, sizeof buffer) == 0, | ||
| 18 | "try to write \"rox-simple\""); | ||
| 19 | } | ||
diff --git a/pintos-progos/tests/userprog/rox-simple.ck b/pintos-progos/tests/userprog/rox-simple.ck new file mode 100644 index 0000000..c9dcc66 --- /dev/null +++ b/pintos-progos/tests/userprog/rox-simple.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (rox-simple) begin | ||
| 7 | (rox-simple) open "rox-simple" | ||
| 8 | (rox-simple) read "rox-simple" | ||
| 9 | (rox-simple) try to write "rox-simple" | ||
| 10 | (rox-simple) end | ||
| 11 | rox-simple: exit(0) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/sample.inc b/pintos-progos/tests/userprog/sample.inc new file mode 100644 index 0000000..59f2bcb --- /dev/null +++ b/pintos-progos/tests/userprog/sample.inc | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | char sample[] = { | ||
| 2 | "\"Amazing Electronic Fact: If you scuffed your feet long enough without\n" | ||
| 3 | " touching anything, you would build up so many electrons that your\n" | ||
| 4 | " finger would explode! But this is nothing to worry about unless you\n" | ||
| 5 | " have carpeting.\" --Dave Barry\n" | ||
| 6 | }; | ||
diff --git a/pintos-progos/tests/userprog/sample.txt b/pintos-progos/tests/userprog/sample.txt new file mode 100644 index 0000000..5050fec --- /dev/null +++ b/pintos-progos/tests/userprog/sample.txt | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | "Amazing Electronic Fact: If you scuffed your feet long enough without | ||
| 2 | touching anything, you would build up so many electrons that your | ||
| 3 | finger would explode! But this is nothing to worry about unless you | ||
| 4 | have carpeting." --Dave Barry | ||
diff --git a/pintos-progos/tests/userprog/sc-bad-arg.c b/pintos-progos/tests/userprog/sc-bad-arg.c new file mode 100644 index 0000000..0b512a0 --- /dev/null +++ b/pintos-progos/tests/userprog/sc-bad-arg.c | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | /* Sticks a system call number (SYS_EXIT) at the very top of the | ||
| 2 | stack, then invokes a system call with the stack pointer | ||
| 3 | (%esp) set to its address. The process must be terminated | ||
| 4 | with -1 exit code because the argument to the system call | ||
| 5 | would be above the top of the user address space. */ | ||
| 6 | |||
| 7 | #include <syscall-nr.h> | ||
| 8 | #include "tests/lib.h" | ||
| 9 | #include "tests/main.h" | ||
| 10 | |||
| 11 | void | ||
| 12 | test_main (void) | ||
| 13 | { | ||
| 14 | asm volatile ("movl $0xbffffffc, %%esp; movl %0, (%%esp); int $0x30" | ||
| 15 | : : "i" (SYS_EXIT)); | ||
| 16 | fail ("should have called exit(-1)"); | ||
| 17 | } | ||
diff --git a/pintos-progos/tests/userprog/sc-bad-arg.ck b/pintos-progos/tests/userprog/sc-bad-arg.ck new file mode 100644 index 0000000..8981105 --- /dev/null +++ b/pintos-progos/tests/userprog/sc-bad-arg.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (sc-bad-arg) begin | ||
| 7 | sc-bad-arg: exit(-1) | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/userprog/sc-bad-sp.c b/pintos-progos/tests/userprog/sc-bad-sp.c new file mode 100644 index 0000000..39cce84 --- /dev/null +++ b/pintos-progos/tests/userprog/sc-bad-sp.c | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | /* Invokes a system call with the stack pointer (%esp) set to a | ||
| 2 | bad address. The process must be terminated with -1 exit | ||
| 3 | code. | ||
| 4 | |||
| 5 | For Project 3: The bad address lies approximately 64MB below | ||
| 6 | the code segment, so there is no ambiguity that this attempt | ||
| 7 | must be rejected even after stack growth is implemented. | ||
| 8 | Moreover, a good stack growth heuristics should probably not | ||
| 9 | grow the stack for the purpose of reading the system call | ||
| 10 | number and arguments. */ | ||
| 11 | |||
| 12 | #include "tests/lib.h" | ||
| 13 | #include "tests/main.h" | ||
| 14 | |||
| 15 | void | ||
| 16 | test_main (void) | ||
| 17 | { | ||
| 18 | asm volatile ("movl $.-(64*1024*1024), %esp; int $0x30"); | ||
| 19 | fail ("should have called exit(-1)"); | ||
| 20 | } | ||
diff --git a/pintos-progos/tests/userprog/sc-bad-sp.ck b/pintos-progos/tests/userprog/sc-bad-sp.ck new file mode 100644 index 0000000..498cec1 --- /dev/null +++ b/pintos-progos/tests/userprog/sc-bad-sp.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (sc-bad-sp) begin | ||
| 7 | sc-bad-sp: exit(-1) | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/userprog/sc-boundary-2.c b/pintos-progos/tests/userprog/sc-boundary-2.c new file mode 100644 index 0000000..8acf036 --- /dev/null +++ b/pintos-progos/tests/userprog/sc-boundary-2.c | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | /* Invokes a system call with one byte of the system call's | ||
| 2 | argument on a separate page from the rest of the bytes. This | ||
| 3 | must work. */ | ||
| 4 | |||
| 5 | #include <syscall-nr.h> | ||
| 6 | #include "tests/userprog/boundary.h" | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | void | ||
| 11 | test_main (void) | ||
| 12 | { | ||
| 13 | /* Make one byte of a syscall argument hang over into a second | ||
| 14 | page. */ | ||
| 15 | int *p = (int *) ((char *) get_boundary_area () - 7); | ||
| 16 | p[0] = SYS_EXIT; | ||
| 17 | p[1] = 67; | ||
| 18 | |||
| 19 | /* Invoke the system call. */ | ||
| 20 | asm volatile ("movl %0, %%esp; int $0x30" : : "g" (p)); | ||
| 21 | fail ("should have called exit(67)"); | ||
| 22 | } | ||
diff --git a/pintos-progos/tests/userprog/sc-boundary-2.ck b/pintos-progos/tests/userprog/sc-boundary-2.ck new file mode 100644 index 0000000..43766bf --- /dev/null +++ b/pintos-progos/tests/userprog/sc-boundary-2.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (sc-boundary-2) begin | ||
| 7 | sc-boundary-2: exit(67) | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/userprog/sc-boundary.c b/pintos-progos/tests/userprog/sc-boundary.c new file mode 100644 index 0000000..d889535 --- /dev/null +++ b/pintos-progos/tests/userprog/sc-boundary.c | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | /* Invokes a system call with the system call number and its | ||
| 2 | argument on separate pages. This must work. */ | ||
| 3 | |||
| 4 | #include <syscall-nr.h> | ||
| 5 | #include "tests/userprog/boundary.h" | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | /* Put a syscall number at the end of one page | ||
| 13 | and its argument at the beginning of another. */ | ||
| 14 | int *p = get_boundary_area (); | ||
| 15 | p--; | ||
| 16 | p[0] = SYS_EXIT; | ||
| 17 | p[1] = 42; | ||
| 18 | |||
| 19 | /* Invoke the system call. */ | ||
| 20 | asm volatile ("movl %0, %%esp; int $0x30" : : "g" (p)); | ||
| 21 | fail ("should have called exit(42)"); | ||
| 22 | } | ||
diff --git a/pintos-progos/tests/userprog/sc-boundary.ck b/pintos-progos/tests/userprog/sc-boundary.ck new file mode 100644 index 0000000..3f7cbaf --- /dev/null +++ b/pintos-progos/tests/userprog/sc-boundary.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (sc-boundary) begin | ||
| 7 | sc-boundary: exit(42) | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/userprog/wait-bad-pid.c b/pintos-progos/tests/userprog/wait-bad-pid.c new file mode 100644 index 0000000..3fe8ee4 --- /dev/null +++ b/pintos-progos/tests/userprog/wait-bad-pid.c | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | /* Waits for an invalid pid. This may fail or terminate the | ||
| 2 | process with -1 exit code. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | wait ((pid_t) 0x0c020301); | ||
| 11 | } | ||
diff --git a/pintos-progos/tests/userprog/wait-bad-pid.ck b/pintos-progos/tests/userprog/wait-bad-pid.ck new file mode 100644 index 0000000..db63fb9 --- /dev/null +++ b/pintos-progos/tests/userprog/wait-bad-pid.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (wait-bad-pid) begin | ||
| 7 | (wait-bad-pid) end | ||
| 8 | wait-bad-pid: exit(0) | ||
| 9 | EOF | ||
| 10 | (wait-bad-pid) begin | ||
| 11 | wait-bad-pid: exit(-1) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/wait-killed.c b/pintos-progos/tests/userprog/wait-killed.c new file mode 100644 index 0000000..6a2a6b5 --- /dev/null +++ b/pintos-progos/tests/userprog/wait-killed.c | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | /* Wait for a process that will be killed for bad behavior. */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | msg ("wait(exec()) = %d", wait (exec ("child-bad"))); | ||
| 11 | } | ||
diff --git a/pintos-progos/tests/userprog/wait-killed.ck b/pintos-progos/tests/userprog/wait-killed.ck new file mode 100644 index 0000000..5df0e9c --- /dev/null +++ b/pintos-progos/tests/userprog/wait-killed.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (wait-killed) begin | ||
| 7 | (child-bad) begin | ||
| 8 | child-bad: exit(-1) | ||
| 9 | (wait-killed) wait(exec()) = -1 | ||
| 10 | (wait-killed) end | ||
| 11 | wait-killed: exit(0) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/wait-simple.c b/pintos-progos/tests/userprog/wait-simple.c new file mode 100644 index 0000000..d3afcf3 --- /dev/null +++ b/pintos-progos/tests/userprog/wait-simple.c | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | /* Wait for a subprocess to finish. */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | msg ("wait(exec()) = %d", wait (exec ("child-simple"))); | ||
| 11 | } | ||
diff --git a/pintos-progos/tests/userprog/wait-simple.ck b/pintos-progos/tests/userprog/wait-simple.ck new file mode 100644 index 0000000..93dd577 --- /dev/null +++ b/pintos-progos/tests/userprog/wait-simple.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (wait-simple) begin | ||
| 7 | (child-simple) run | ||
| 8 | child-simple: exit(81) | ||
| 9 | (wait-simple) wait(exec()) = 81 | ||
| 10 | (wait-simple) end | ||
| 11 | wait-simple: exit(0) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/wait-twice.c b/pintos-progos/tests/userprog/wait-twice.c new file mode 100644 index 0000000..785e684 --- /dev/null +++ b/pintos-progos/tests/userprog/wait-twice.c | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | /* Wait for a subprocess to finish, twice. | ||
| 2 | The first call must wait in the usual way and return the exit code. | ||
| 3 | The second wait call must return -1 immediately. */ | ||
| 4 | |||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | pid_t child = exec ("child-simple"); | ||
| 13 | msg ("wait(exec()) = %d", wait (child)); | ||
| 14 | msg ("wait(exec()) = %d", wait (child)); | ||
| 15 | } | ||
diff --git a/pintos-progos/tests/userprog/wait-twice.ck b/pintos-progos/tests/userprog/wait-twice.ck new file mode 100644 index 0000000..6d53843 --- /dev/null +++ b/pintos-progos/tests/userprog/wait-twice.ck | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (wait-twice) begin | ||
| 7 | (child-simple) run | ||
| 8 | child-simple: exit(81) | ||
| 9 | (wait-twice) wait(exec()) = 81 | ||
| 10 | (wait-twice) wait(exec()) = -1 | ||
| 11 | (wait-twice) end | ||
| 12 | wait-twice: exit(0) | ||
| 13 | EOF | ||
| 14 | pass; | ||
diff --git a/pintos-progos/tests/userprog/write-bad-fd.c b/pintos-progos/tests/userprog/write-bad-fd.c new file mode 100644 index 0000000..f3b1151 --- /dev/null +++ b/pintos-progos/tests/userprog/write-bad-fd.c | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | /* Tries to write to an invalid fd, | ||
| 2 | which must either fail silently or terminate the process with | ||
| 3 | exit code -1. */ | ||
| 4 | |||
| 5 | #include <limits.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | char buf = 123; | ||
| 13 | write (0x01012342, &buf, 1); | ||
| 14 | write (7, &buf, 1); | ||
| 15 | write (2546, &buf, 1); | ||
| 16 | write (-5, &buf, 1); | ||
| 17 | write (-8192, &buf, 1); | ||
| 18 | write (INT_MIN + 1, &buf, 1); | ||
| 19 | write (INT_MAX - 1, &buf, 1); | ||
| 20 | } | ||
diff --git a/pintos-progos/tests/userprog/write-bad-fd.ck b/pintos-progos/tests/userprog/write-bad-fd.ck new file mode 100644 index 0000000..8da7a8b --- /dev/null +++ b/pintos-progos/tests/userprog/write-bad-fd.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (write-bad-fd) begin | ||
| 7 | (write-bad-fd) end | ||
| 8 | write-bad-fd: exit(0) | ||
| 9 | EOF | ||
| 10 | (write-bad-fd) begin | ||
| 11 | write-bad-fd: exit(-1) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/write-bad-ptr.c b/pintos-progos/tests/userprog/write-bad-ptr.c new file mode 100644 index 0000000..5336479 --- /dev/null +++ b/pintos-progos/tests/userprog/write-bad-ptr.c | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | /* Passes an invalid pointer to the write system call. | ||
| 2 | The process must be terminated with -1 exit code. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | int handle; | ||
| 12 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 13 | |||
| 14 | write (handle, (char *) 0x10123420, 123); | ||
| 15 | fail ("should have exited with -1"); | ||
| 16 | } | ||
diff --git a/pintos-progos/tests/userprog/write-bad-ptr.ck b/pintos-progos/tests/userprog/write-bad-ptr.ck new file mode 100644 index 0000000..ad9f399 --- /dev/null +++ b/pintos-progos/tests/userprog/write-bad-ptr.ck | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (write-bad-ptr) begin | ||
| 7 | (write-bad-ptr) open "sample.txt" | ||
| 8 | (write-bad-ptr) end | ||
| 9 | write-bad-ptr: exit(0) | ||
| 10 | EOF | ||
| 11 | (write-bad-ptr) begin | ||
| 12 | (write-bad-ptr) open "sample.txt" | ||
| 13 | write-bad-ptr: exit(-1) | ||
| 14 | EOF | ||
| 15 | pass; | ||
diff --git a/pintos-progos/tests/userprog/write-boundary.c b/pintos-progos/tests/userprog/write-boundary.c new file mode 100644 index 0000000..d2de1d4 --- /dev/null +++ b/pintos-progos/tests/userprog/write-boundary.c | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | /* Writes data spanning two pages in virtual address space, | ||
| 2 | which must succeed. */ | ||
| 3 | |||
| 4 | #include <string.h> | ||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/userprog/boundary.h" | ||
| 7 | #include "tests/userprog/sample.inc" | ||
| 8 | #include "tests/lib.h" | ||
| 9 | #include "tests/main.h" | ||
| 10 | |||
| 11 | void | ||
| 12 | test_main (void) | ||
| 13 | { | ||
| 14 | int handle; | ||
| 15 | int byte_cnt; | ||
| 16 | char *sample_p; | ||
| 17 | |||
| 18 | sample_p = copy_string_across_boundary (sample); | ||
| 19 | |||
| 20 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 21 | |||
| 22 | byte_cnt = write (handle, sample_p, sizeof sample - 1); | ||
| 23 | if (byte_cnt != sizeof sample - 1) | ||
| 24 | fail ("write() returned %d instead of %zu", byte_cnt, sizeof sample - 1); | ||
| 25 | } | ||
diff --git a/pintos-progos/tests/userprog/write-boundary.ck b/pintos-progos/tests/userprog/write-boundary.ck new file mode 100644 index 0000000..7883781 --- /dev/null +++ b/pintos-progos/tests/userprog/write-boundary.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (write-boundary) begin | ||
| 7 | (write-boundary) open "sample.txt" | ||
| 8 | (write-boundary) end | ||
| 9 | write-boundary: exit(0) | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/userprog/write-normal.c b/pintos-progos/tests/userprog/write-normal.c new file mode 100644 index 0000000..e0297aa --- /dev/null +++ b/pintos-progos/tests/userprog/write-normal.c | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | /* Try writing a file in the most normal way. */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/userprog/sample.inc" | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | int handle, byte_cnt; | ||
| 12 | |||
| 13 | CHECK (create ("test.txt", sizeof sample - 1), "create \"test.txt\""); | ||
| 14 | CHECK ((handle = open ("test.txt")) > 1, "open \"test.txt\""); | ||
| 15 | |||
| 16 | byte_cnt = write (handle, sample, sizeof sample - 1); | ||
| 17 | if (byte_cnt != sizeof sample - 1) | ||
| 18 | fail ("write() returned %d instead of %zu", byte_cnt, sizeof sample - 1); | ||
| 19 | } | ||
| 20 | |||
diff --git a/pintos-progos/tests/userprog/write-normal.ck b/pintos-progos/tests/userprog/write-normal.ck new file mode 100644 index 0000000..9fa6024 --- /dev/null +++ b/pintos-progos/tests/userprog/write-normal.ck | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (write-normal) begin | ||
| 7 | (write-normal) create "test.txt" | ||
| 8 | (write-normal) open "test.txt" | ||
| 9 | (write-normal) end | ||
| 10 | write-normal: exit(0) | ||
| 11 | EOF | ||
| 12 | pass; | ||
diff --git a/pintos-progos/tests/userprog/write-stdin.c b/pintos-progos/tests/userprog/write-stdin.c new file mode 100644 index 0000000..491ea53 --- /dev/null +++ b/pintos-progos/tests/userprog/write-stdin.c | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | /* Try writing to fd 0 (stdin), | ||
| 2 | which may just fail or terminate the process with -1 exit | ||
| 3 | code. */ | ||
| 4 | |||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | char buf = 123; | ||
| 13 | write (0, &buf, 1); | ||
| 14 | } | ||
diff --git a/pintos-progos/tests/userprog/write-stdin.ck b/pintos-progos/tests/userprog/write-stdin.ck new file mode 100644 index 0000000..a6caf81 --- /dev/null +++ b/pintos-progos/tests/userprog/write-stdin.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (write-stdin) begin | ||
| 7 | (write-stdin) end | ||
| 8 | write-stdin: exit(0) | ||
| 9 | EOF | ||
| 10 | (write-stdin) begin | ||
| 11 | write-stdin: exit(-1) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/write-zero.c b/pintos-progos/tests/userprog/write-zero.c new file mode 100644 index 0000000..d8dac9b --- /dev/null +++ b/pintos-progos/tests/userprog/write-zero.c | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | /* Try a 0-byte write, which should return 0 without writing | ||
| 2 | anything. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | int handle, byte_cnt; | ||
| 12 | char buf; | ||
| 13 | |||
| 14 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 15 | |||
| 16 | buf = 123; | ||
| 17 | byte_cnt = write (handle, &buf, 0); | ||
| 18 | if (byte_cnt != 0) | ||
| 19 | fail("write() returned %d instead of 0", byte_cnt); | ||
| 20 | } | ||
diff --git a/pintos-progos/tests/userprog/write-zero.ck b/pintos-progos/tests/userprog/write-zero.ck new file mode 100644 index 0000000..cc4cd60 --- /dev/null +++ b/pintos-progos/tests/userprog/write-zero.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (write-zero) begin | ||
| 7 | (write-zero) open "sample.txt" | ||
| 8 | (write-zero) end | ||
| 9 | write-zero: exit(0) | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/vm/Grading b/pintos-progos/tests/vm/Grading new file mode 100644 index 0000000..f0c2c13 --- /dev/null +++ b/pintos-progos/tests/vm/Grading | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | # Percentage of the testing point total designated for each set of | ||
| 2 | # tests. | ||
| 3 | |||
| 4 | # This project is primarily about virtual memory, but all the previous | ||
| 5 | # functionality should work too, and it's easy to screw it up, thus | ||
| 6 | # the equal weight placed on each. | ||
| 7 | |||
| 8 | 50% tests/vm/Rubric.functionality | ||
| 9 | 15% tests/vm/Rubric.robustness | ||
| 10 | 10% tests/userprog/Rubric.functionality | ||
| 11 | 5% tests/userprog/Rubric.robustness | ||
| 12 | 20% tests/filesys/base/Rubric | ||
diff --git a/pintos-progos/tests/vm/Make.tests b/pintos-progos/tests/vm/Make.tests new file mode 100644 index 0000000..0307ccc --- /dev/null +++ b/pintos-progos/tests/vm/Make.tests | |||
| @@ -0,0 +1,107 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | |||
| 3 | tests/vm_TESTS = $(addprefix tests/vm/,pt-grow-stack pt-grow-pusha \ | ||
| 4 | pt-grow-bad pt-big-stk-obj pt-bad-addr pt-bad-read pt-write-code \ | ||
| 5 | pt-write-code2 pt-grow-stk-sc mmap-read \ | ||
| 6 | mmap-close mmap-unmap mmap-overlap mmap-twice mmap-write mmap-exit \ | ||
| 7 | mmap-shuffle mmap-bad-fd mmap-clean mmap-inherit mmap-misalign \ | ||
| 8 | mmap-null mmap-over-code mmap-over-data mmap-over-stk mmap-remove \ | ||
| 9 | mmap-zero mmap-lazy-seq) | ||
| 10 | |||
| 11 | # Deactivated | ||
| 12 | PAGE_TESTS=page-linear page-parallel page-merge-seq \ | ||
| 13 | page-merge-par page-merge-stk page-merge-mm page-shuffle | ||
| 14 | |||
| 15 | tests/vm_PROGS = $(tests/vm_TESTS) $(addprefix tests/vm/,child-linear \ | ||
| 16 | child-sort child-qsort child-qsort-mm child-mm-wrt child-inherit) | ||
| 17 | |||
| 18 | tests/vm/pt-grow-stack_SRC = tests/vm/pt-grow-stack.c tests/arc4.c \ | ||
| 19 | tests/cksum.c tests/lib.c tests/main.c | ||
| 20 | tests/vm/pt-grow-pusha_SRC = tests/vm/pt-grow-pusha.c tests/lib.c \ | ||
| 21 | tests/main.c | ||
| 22 | tests/vm/pt-grow-bad_SRC = tests/vm/pt-grow-bad.c tests/lib.c tests/main.c | ||
| 23 | tests/vm/pt-big-stk-obj_SRC = tests/vm/pt-big-stk-obj.c tests/arc4.c \ | ||
| 24 | tests/cksum.c tests/lib.c tests/main.c | ||
| 25 | tests/vm/pt-bad-addr_SRC = tests/vm/pt-bad-addr.c tests/lib.c tests/main.c | ||
| 26 | tests/vm/pt-bad-read_SRC = tests/vm/pt-bad-read.c tests/lib.c tests/main.c | ||
| 27 | tests/vm/pt-write-code_SRC = tests/vm/pt-write-code.c tests/lib.c tests/main.c | ||
| 28 | tests/vm/pt-write-code2_SRC = tests/vm/pt-write-code-2.c tests/lib.c tests/main.c | ||
| 29 | tests/vm/pt-grow-stk-sc_SRC = tests/vm/pt-grow-stk-sc.c tests/lib.c tests/main.c | ||
| 30 | tests/vm/page-linear_SRC = tests/vm/page-linear.c tests/arc4.c \ | ||
| 31 | tests/lib.c tests/main.c | ||
| 32 | tests/vm/page-parallel_SRC = tests/vm/page-parallel.c tests/lib.c tests/main.c | ||
| 33 | tests/vm/page-merge-seq_SRC = tests/vm/page-merge-seq.c tests/arc4.c \ | ||
| 34 | tests/lib.c tests/main.c | ||
| 35 | tests/vm/page-merge-par_SRC = tests/vm/page-merge-par.c \ | ||
| 36 | tests/vm/parallel-merge.c tests/arc4.c tests/lib.c tests/main.c | ||
| 37 | tests/vm/page-merge-stk_SRC = tests/vm/page-merge-stk.c \ | ||
| 38 | tests/vm/parallel-merge.c tests/arc4.c tests/lib.c tests/main.c | ||
| 39 | tests/vm/page-merge-mm_SRC = tests/vm/page-merge-mm.c \ | ||
| 40 | tests/vm/parallel-merge.c tests/arc4.c tests/lib.c tests/main.c | ||
| 41 | tests/vm/page-shuffle_SRC = tests/vm/page-shuffle.c tests/arc4.c \ | ||
| 42 | tests/cksum.c tests/lib.c tests/main.c | ||
| 43 | tests/vm/mmap-read_SRC = tests/vm/mmap-read.c tests/lib.c tests/main.c | ||
| 44 | tests/vm/mmap-close_SRC = tests/vm/mmap-close.c tests/lib.c tests/main.c | ||
| 45 | tests/vm/mmap-unmap_SRC = tests/vm/mmap-unmap.c tests/lib.c tests/main.c | ||
| 46 | tests/vm/mmap-overlap_SRC = tests/vm/mmap-overlap.c tests/lib.c tests/main.c | ||
| 47 | tests/vm/mmap-twice_SRC = tests/vm/mmap-twice.c tests/lib.c tests/main.c | ||
| 48 | tests/vm/mmap-write_SRC = tests/vm/mmap-write.c tests/lib.c tests/main.c | ||
| 49 | tests/vm/mmap-lazy-seq_SRC = tests/vm/mmap-lazy-seq.c tests/lib.c tests/main.c | ||
| 50 | tests/vm/mmap-exit_SRC = tests/vm/mmap-exit.c tests/lib.c tests/main.c | ||
| 51 | tests/vm/mmap-shuffle_SRC = tests/vm/mmap-shuffle.c tests/arc4.c \ | ||
| 52 | tests/cksum.c tests/lib.c tests/main.c | ||
| 53 | tests/vm/mmap-bad-fd_SRC = tests/vm/mmap-bad-fd.c tests/lib.c tests/main.c | ||
| 54 | tests/vm/mmap-clean_SRC = tests/vm/mmap-clean.c tests/lib.c tests/main.c | ||
| 55 | tests/vm/mmap-inherit_SRC = tests/vm/mmap-inherit.c tests/lib.c tests/main.c | ||
| 56 | tests/vm/mmap-misalign_SRC = tests/vm/mmap-misalign.c tests/lib.c \ | ||
| 57 | tests/main.c | ||
| 58 | tests/vm/mmap-null_SRC = tests/vm/mmap-null.c tests/lib.c tests/main.c | ||
| 59 | tests/vm/mmap-over-code_SRC = tests/vm/mmap-over-code.c tests/lib.c \ | ||
| 60 | tests/main.c | ||
| 61 | tests/vm/mmap-over-data_SRC = tests/vm/mmap-over-data.c tests/lib.c \ | ||
| 62 | tests/main.c | ||
| 63 | tests/vm/mmap-over-stk_SRC = tests/vm/mmap-over-stk.c tests/lib.c tests/main.c | ||
| 64 | tests/vm/mmap-remove_SRC = tests/vm/mmap-remove.c tests/lib.c tests/main.c | ||
| 65 | tests/vm/mmap-zero_SRC = tests/vm/mmap-zero.c tests/lib.c tests/main.c | ||
| 66 | |||
| 67 | tests/vm/child-linear_SRC = tests/vm/child-linear.c tests/arc4.c tests/lib.c | ||
| 68 | tests/vm/child-qsort_SRC = tests/vm/child-qsort.c tests/vm/qsort.c tests/lib.c | ||
| 69 | tests/vm/child-qsort-mm_SRC = tests/vm/child-qsort-mm.c tests/vm/qsort.c \ | ||
| 70 | tests/lib.c | ||
| 71 | tests/vm/child-sort_SRC = tests/vm/child-sort.c tests/lib.c | ||
| 72 | tests/vm/child-mm-wrt_SRC = tests/vm/child-mm-wrt.c tests/lib.c tests/main.c | ||
| 73 | tests/vm/child-inherit_SRC = tests/vm/child-inherit.c tests/lib.c tests/main.c | ||
| 74 | |||
| 75 | tests/vm/pt-bad-read_PUTFILES = tests/vm/sample.txt | ||
| 76 | tests/vm/pt-write-code2_PUTFILES = tests/vm/sample.txt | ||
| 77 | tests/vm/mmap-close_PUTFILES = tests/vm/sample.txt | ||
| 78 | tests/vm/mmap-read_PUTFILES = tests/vm/sample.txt | ||
| 79 | tests/vm/mmap-unmap_PUTFILES = tests/vm/sample.txt | ||
| 80 | tests/vm/mmap-twice_PUTFILES = tests/vm/sample.txt | ||
| 81 | tests/vm/mmap-overlap_PUTFILES = tests/vm/zeros | ||
| 82 | tests/vm/mmap-exit_PUTFILES = tests/vm/child-mm-wrt | ||
| 83 | tests/vm/page-parallel_PUTFILES = tests/vm/child-linear | ||
| 84 | tests/vm/page-merge-seq_PUTFILES = tests/vm/child-sort | ||
| 85 | tests/vm/page-merge-par_PUTFILES = tests/vm/child-sort | ||
| 86 | tests/vm/page-merge-stk_PUTFILES = tests/vm/child-qsort | ||
| 87 | tests/vm/page-merge-mm_PUTFILES = tests/vm/child-qsort-mm | ||
| 88 | tests/vm/mmap-clean_PUTFILES = tests/vm/sample.txt | ||
| 89 | tests/vm/mmap-inherit_PUTFILES = tests/vm/sample.txt tests/vm/child-inherit | ||
| 90 | tests/vm/mmap-misalign_PUTFILES = tests/vm/sample.txt | ||
| 91 | tests/vm/mmap-null_PUTFILES = tests/vm/sample.txt | ||
| 92 | tests/vm/mmap-over-code_PUTFILES = tests/vm/sample.txt | ||
| 93 | tests/vm/mmap-over-data_PUTFILES = tests/vm/sample.txt | ||
| 94 | tests/vm/mmap-over-stk_PUTFILES = tests/vm/sample.txt | ||
| 95 | tests/vm/mmap-remove_PUTFILES = tests/vm/sample.txt | ||
| 96 | |||
| 97 | tests/vm/page-linear.output: TIMEOUT = 300 | ||
| 98 | tests/vm/page-shuffle.output: TIMEOUT = 600 | ||
| 99 | tests/vm/mmap-shuffle.output: TIMEOUT = 600 | ||
| 100 | tests/vm/page-merge-seq.output: TIMEOUT = 600 | ||
| 101 | tests/vm/page-merge-par.output: TIMEOUT = 600 | ||
| 102 | |||
| 103 | tests/vm/zeros: | ||
| 104 | dd if=/dev/zero of=$@ bs=1024 count=6 | ||
| 105 | |||
| 106 | clean:: | ||
| 107 | rm -f tests/vm/zeros | ||
diff --git a/pintos-progos/tests/vm/Rubric.functionality b/pintos-progos/tests/vm/Rubric.functionality new file mode 100644 index 0000000..ede0221 --- /dev/null +++ b/pintos-progos/tests/vm/Rubric.functionality | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | Functionality of virtual memory subsystem: | ||
| 2 | - Test stack growth. | ||
| 3 | 3 pt-grow-stack | ||
| 4 | 3 pt-grow-stk-sc | ||
| 5 | 3 pt-big-stk-obj | ||
| 6 | 3 pt-grow-pusha | ||
| 7 | |||
| 8 | - Test "mmap" system call. | ||
| 9 | 2 mmap-read | ||
| 10 | 2 mmap-write | ||
| 11 | 2 mmap-shuffle | ||
| 12 | |||
| 13 | 2 mmap-twice | ||
| 14 | |||
| 15 | 2 mmap-unmap | ||
| 16 | 1 mmap-exit | ||
| 17 | |||
| 18 | 3 mmap-clean | ||
| 19 | |||
| 20 | 2 mmap-close | ||
| 21 | 2 mmap-remove | ||
diff --git a/pintos-progos/tests/vm/Rubric.paging b/pintos-progos/tests/vm/Rubric.paging new file mode 100644 index 0000000..cf1d871 --- /dev/null +++ b/pintos-progos/tests/vm/Rubric.paging | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | - Test paging behavior. | ||
| 2 | 3 page-linear | ||
| 3 | 3 page-parallel | ||
| 4 | 3 page-shuffle | ||
| 5 | 4 page-merge-seq | ||
| 6 | 4 page-merge-par | ||
| 7 | 4 page-merge-mm | ||
| 8 | 4 page-merge-stk | ||
diff --git a/pintos-progos/tests/vm/Rubric.robustness b/pintos-progos/tests/vm/Rubric.robustness new file mode 100644 index 0000000..eca0af4 --- /dev/null +++ b/pintos-progos/tests/vm/Rubric.robustness | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | Robustness of virtual memory subsystem: | ||
| 2 | - Test robustness of page table support. | ||
| 3 | 2 pt-bad-addr | ||
| 4 | 3 pt-bad-read | ||
| 5 | 2 pt-write-code | ||
| 6 | 3 pt-write-code2 | ||
| 7 | 4 pt-grow-bad | ||
| 8 | |||
| 9 | - Test robustness of "mmap" system call. | ||
| 10 | 1 mmap-bad-fd | ||
| 11 | 1 mmap-inherit | ||
| 12 | 1 mmap-null | ||
| 13 | 1 mmap-zero | ||
| 14 | |||
| 15 | 2 mmap-misalign | ||
| 16 | |||
| 17 | 2 mmap-over-code | ||
| 18 | 2 mmap-over-data | ||
| 19 | 2 mmap-over-stk | ||
| 20 | 2 mmap-overlap | ||
| 21 | 4 mmap-lazy-seq | ||
diff --git a/pintos-progos/tests/vm/child-inherit.c b/pintos-progos/tests/vm/child-inherit.c new file mode 100644 index 0000000..d3186a1 --- /dev/null +++ b/pintos-progos/tests/vm/child-inherit.c | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | /* Child process for mmap-inherit test. | ||
| 2 | Tries to write to a mapping present in the parent. | ||
| 3 | The process must be terminated with -1 exit code. */ | ||
| 4 | |||
| 5 | #include <string.h> | ||
| 6 | #include "tests/vm/sample.inc" | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | void | ||
| 11 | test_main (void) | ||
| 12 | { | ||
| 13 | memset ((char *) 0x54321000, 0, 4096); | ||
| 14 | fail ("child can modify parent's memory mappings"); | ||
| 15 | } | ||
| 16 | |||
diff --git a/pintos-progos/tests/vm/child-linear.c b/pintos-progos/tests/vm/child-linear.c new file mode 100644 index 0000000..eca3e3f --- /dev/null +++ b/pintos-progos/tests/vm/child-linear.c | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | /* Child process of page-parallel. | ||
| 2 | Encrypts 1 MB of zeros, then decrypts it, and ensures that | ||
| 3 | the zeros are back. */ | ||
| 4 | |||
| 5 | #include <string.h> | ||
| 6 | #include "tests/arc4.h" | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | const char *test_name = "child-linear"; | ||
| 11 | |||
| 12 | #define SIZE (1024 * 1024) | ||
| 13 | static char buf[SIZE]; | ||
| 14 | |||
| 15 | int | ||
| 16 | main (int argc, char *argv[]) | ||
| 17 | { | ||
| 18 | const char *key = argv[argc - 1]; | ||
| 19 | struct arc4 arc4; | ||
| 20 | size_t i; | ||
| 21 | |||
| 22 | /* Encrypt zeros. */ | ||
| 23 | arc4_init (&arc4, key, strlen (key)); | ||
| 24 | arc4_crypt (&arc4, buf, SIZE); | ||
| 25 | |||
| 26 | /* Decrypt back to zeros. */ | ||
| 27 | arc4_init (&arc4, key, strlen (key)); | ||
| 28 | arc4_crypt (&arc4, buf, SIZE); | ||
| 29 | |||
| 30 | /* Check that it's all zeros. */ | ||
| 31 | for (i = 0; i < SIZE; i++) | ||
| 32 | if (buf[i] != '\0') | ||
| 33 | fail ("byte %zu != 0", i); | ||
| 34 | |||
| 35 | return 0x42; | ||
| 36 | } | ||
diff --git a/pintos-progos/tests/vm/child-mm-wrt.c b/pintos-progos/tests/vm/child-mm-wrt.c new file mode 100644 index 0000000..8419788 --- /dev/null +++ b/pintos-progos/tests/vm/child-mm-wrt.c | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | /* Child process of mmap-exit. | ||
| 2 | Mmaps a file and writes to it via the mmap'ing, then exits | ||
| 3 | without calling munmap. The data in the mapped region must be | ||
| 4 | written out at program termination. */ | ||
| 5 | |||
| 6 | #include <string.h> | ||
| 7 | #include <syscall.h> | ||
| 8 | #include "tests/vm/sample.inc" | ||
| 9 | #include "tests/lib.h" | ||
| 10 | #include "tests/main.h" | ||
| 11 | |||
| 12 | #define ACTUAL ((void *) 0x10000000) | ||
| 13 | |||
| 14 | void | ||
| 15 | test_main (void) | ||
| 16 | { | ||
| 17 | int handle; | ||
| 18 | |||
| 19 | CHECK (create ("sample.txt", sizeof sample), "create \"sample.txt\""); | ||
| 20 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 21 | CHECK (mmap (handle, ACTUAL) != MAP_FAILED, "mmap \"sample.txt\""); | ||
| 22 | memcpy (ACTUAL, sample, sizeof sample); | ||
| 23 | } | ||
| 24 | |||
diff --git a/pintos-progos/tests/vm/child-qsort-mm.c b/pintos-progos/tests/vm/child-qsort-mm.c new file mode 100644 index 0000000..db45499 --- /dev/null +++ b/pintos-progos/tests/vm/child-qsort-mm.c | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | /* Mmaps a 128 kB file "sorts" the bytes in it, using quick sort, | ||
| 2 | a multi-pass divide and conquer algorithm. */ | ||
| 3 | |||
| 4 | #include <debug.h> | ||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | #include "tests/vm/qsort.h" | ||
| 9 | |||
| 10 | const char *test_name = "child-qsort-mm"; | ||
| 11 | |||
| 12 | int | ||
| 13 | main (int argc UNUSED, char *argv[]) | ||
| 14 | { | ||
| 15 | int handle; | ||
| 16 | unsigned char *p = (unsigned char *) 0x10000000; | ||
| 17 | |||
| 18 | quiet = true; | ||
| 19 | |||
| 20 | CHECK ((handle = open (argv[1])) > 1, "open \"%s\"", argv[1]); | ||
| 21 | CHECK (mmap (handle, p) != MAP_FAILED, "mmap \"%s\"", argv[1]); | ||
| 22 | qsort_bytes (p, 1024 * 128); | ||
| 23 | |||
| 24 | return 80; | ||
| 25 | } | ||
diff --git a/pintos-progos/tests/vm/child-qsort.c b/pintos-progos/tests/vm/child-qsort.c new file mode 100644 index 0000000..355f4eb --- /dev/null +++ b/pintos-progos/tests/vm/child-qsort.c | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | /* Reads a 128 kB file onto the stack and "sorts" the bytes in | ||
| 2 | it, using quick sort, a multi-pass divide and conquer | ||
| 3 | algorithm. The sorted data is written back to the same file | ||
| 4 | in-place. */ | ||
| 5 | |||
| 6 | #include <debug.h> | ||
| 7 | #include <syscall.h> | ||
| 8 | #include "tests/lib.h" | ||
| 9 | #include "tests/main.h" | ||
| 10 | #include "tests/vm/qsort.h" | ||
| 11 | |||
| 12 | const char *test_name = "child-qsort"; | ||
| 13 | |||
| 14 | int | ||
| 15 | main (int argc UNUSED, char *argv[]) | ||
| 16 | { | ||
| 17 | int handle; | ||
| 18 | unsigned char buf[128 * 1024]; | ||
| 19 | size_t size; | ||
| 20 | |||
| 21 | quiet = true; | ||
| 22 | |||
| 23 | CHECK ((handle = open (argv[1])) > 1, "open \"%s\"", argv[1]); | ||
| 24 | |||
| 25 | size = read (handle, buf, sizeof buf); | ||
| 26 | qsort_bytes (buf, sizeof buf); | ||
| 27 | seek (handle, 0); | ||
| 28 | write (handle, buf, size); | ||
| 29 | close (handle); | ||
| 30 | |||
| 31 | return 72; | ||
| 32 | } | ||
diff --git a/pintos-progos/tests/vm/child-sort.c b/pintos-progos/tests/vm/child-sort.c new file mode 100644 index 0000000..dff2c77 --- /dev/null +++ b/pintos-progos/tests/vm/child-sort.c | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | /* Reads a 128 kB file into static data and "sorts" the bytes in | ||
| 2 | it, using counting sort, a single-pass algorithm. The sorted | ||
| 3 | data is written back to the same file in-place. */ | ||
| 4 | |||
| 5 | #include <debug.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | const char *test_name = "child-sort"; | ||
| 11 | |||
| 12 | unsigned char buf[128 * 1024]; | ||
| 13 | size_t histogram[256]; | ||
| 14 | |||
| 15 | int | ||
| 16 | main (int argc UNUSED, char *argv[]) | ||
| 17 | { | ||
| 18 | int handle; | ||
| 19 | unsigned char *p; | ||
| 20 | size_t size; | ||
| 21 | size_t i; | ||
| 22 | |||
| 23 | quiet = true; | ||
| 24 | |||
| 25 | CHECK ((handle = open (argv[1])) > 1, "open \"%s\"", argv[1]); | ||
| 26 | |||
| 27 | size = read (handle, buf, sizeof buf); | ||
| 28 | for (i = 0; i < size; i++) | ||
| 29 | histogram[buf[i]]++; | ||
| 30 | p = buf; | ||
| 31 | for (i = 0; i < sizeof histogram / sizeof *histogram; i++) | ||
| 32 | { | ||
| 33 | size_t j = histogram[i]; | ||
| 34 | while (j-- > 0) | ||
| 35 | *p++ = i; | ||
| 36 | } | ||
| 37 | seek (handle, 0); | ||
| 38 | write (handle, buf, size); | ||
| 39 | close (handle); | ||
| 40 | |||
| 41 | return 123; | ||
| 42 | } | ||
diff --git a/pintos-progos/tests/vm/mmap-bad-fd.c b/pintos-progos/tests/vm/mmap-bad-fd.c new file mode 100644 index 0000000..76a7b50 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-bad-fd.c | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | /* Tries to mmap an invalid fd, | ||
| 2 | which must either fail silently or terminate the process with | ||
| 3 | exit code -1. */ | ||
| 4 | |||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | CHECK (mmap (0x5678, (void *) 0x10000000) == MAP_FAILED, | ||
| 13 | "try to mmap invalid fd"); | ||
| 14 | } | ||
| 15 | |||
diff --git a/pintos-progos/tests/vm/mmap-bad-fd.ck b/pintos-progos/tests/vm/mmap-bad-fd.ck new file mode 100644 index 0000000..f3f58d5 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-bad-fd.ck | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (mmap-bad-fd) begin | ||
| 7 | (mmap-bad-fd) try to mmap invalid fd | ||
| 8 | (mmap-bad-fd) end | ||
| 9 | mmap-bad-fd: exit(0) | ||
| 10 | EOF | ||
| 11 | (mmap-bad-fd) begin | ||
| 12 | (mmap-bad-fd) try to mmap invalid fd | ||
| 13 | mmap-bad-fd: exit(-1) | ||
| 14 | EOF | ||
| 15 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-clean.c b/pintos-progos/tests/vm/mmap-clean.c new file mode 100644 index 0000000..ea1dc9c --- /dev/null +++ b/pintos-progos/tests/vm/mmap-clean.c | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | /* Verifies that mmap'd regions are only written back on munmap | ||
| 2 | if the data was actually modified in memory. */ | ||
| 3 | |||
| 4 | #include <string.h> | ||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/vm/sample.inc" | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | void | ||
| 11 | test_main (void) | ||
| 12 | { | ||
| 13 | static const char overwrite[] = "Now is the time for all good..."; | ||
| 14 | static char buffer[sizeof sample - 1]; | ||
| 15 | char *actual = (char *) 0x54321000; | ||
| 16 | int handle; | ||
| 17 | mapid_t map; | ||
| 18 | |||
| 19 | /* Open file, map, verify data. */ | ||
| 20 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 21 | CHECK ((map = mmap (handle, actual)) != MAP_FAILED, "mmap \"sample.txt\""); | ||
| 22 | if (memcmp (actual, sample, strlen (sample))) | ||
| 23 | fail ("read of mmap'd file reported bad data"); | ||
| 24 | |||
| 25 | /* Modify file. */ | ||
| 26 | CHECK (write (handle, overwrite, strlen (overwrite)) | ||
| 27 | == (int) strlen (overwrite), | ||
| 28 | "write \"sample.txt\""); | ||
| 29 | |||
| 30 | /* Close mapping. Data should not be written back, because we | ||
| 31 | didn't modify it via the mapping. */ | ||
| 32 | msg ("munmap \"sample.txt\""); | ||
| 33 | munmap (map); | ||
| 34 | |||
| 35 | /* Read file back. */ | ||
| 36 | msg ("seek \"sample.txt\""); | ||
| 37 | seek (handle, 0); | ||
| 38 | CHECK (read (handle, buffer, sizeof buffer) == sizeof buffer, | ||
| 39 | "read \"sample.txt\""); | ||
| 40 | |||
| 41 | /* Verify that file overwrite worked. */ | ||
| 42 | if (memcmp (buffer, overwrite, strlen (overwrite)) | ||
| 43 | || memcmp (buffer + strlen (overwrite), sample + strlen (overwrite), | ||
| 44 | strlen (sample) - strlen (overwrite))) | ||
| 45 | { | ||
| 46 | if (!memcmp (buffer, sample, strlen (sample))) | ||
| 47 | fail ("munmap wrote back clean page"); | ||
| 48 | else | ||
| 49 | fail ("read surprising data from file"); | ||
| 50 | } | ||
| 51 | else | ||
| 52 | msg ("file change was retained after munmap"); | ||
| 53 | } | ||
diff --git a/pintos-progos/tests/vm/mmap-clean.ck b/pintos-progos/tests/vm/mmap-clean.ck new file mode 100644 index 0000000..1666d6c --- /dev/null +++ b/pintos-progos/tests/vm/mmap-clean.ck | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (mmap-clean) begin | ||
| 7 | (mmap-clean) open "sample.txt" | ||
| 8 | (mmap-clean) mmap "sample.txt" | ||
| 9 | (mmap-clean) write "sample.txt" | ||
| 10 | (mmap-clean) munmap "sample.txt" | ||
| 11 | (mmap-clean) seek "sample.txt" | ||
| 12 | (mmap-clean) read "sample.txt" | ||
| 13 | (mmap-clean) file change was retained after munmap | ||
| 14 | (mmap-clean) end | ||
| 15 | EOF | ||
| 16 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-close.c b/pintos-progos/tests/vm/mmap-close.c new file mode 100644 index 0000000..d016ee3 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-close.c | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | /* Verifies that memory mappings persist after file close. */ | ||
| 2 | |||
| 3 | #include <string.h> | ||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/vm/sample.inc" | ||
| 6 | #include "tests/arc4.h" | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | #define ACTUAL ((void *) 0x10000000) | ||
| 11 | |||
| 12 | void | ||
| 13 | test_main (void) | ||
| 14 | { | ||
| 15 | int handle; | ||
| 16 | mapid_t map; | ||
| 17 | |||
| 18 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 19 | CHECK ((map = mmap (handle, ACTUAL)) != MAP_FAILED, "mmap \"sample.txt\""); | ||
| 20 | |||
| 21 | close (handle); | ||
| 22 | |||
| 23 | if (memcmp (ACTUAL, sample, strlen (sample))) | ||
| 24 | fail ("read of mmap'd file reported bad data"); | ||
| 25 | |||
| 26 | munmap (map); | ||
| 27 | } | ||
diff --git a/pintos-progos/tests/vm/mmap-close.ck b/pintos-progos/tests/vm/mmap-close.ck new file mode 100644 index 0000000..d15e41a --- /dev/null +++ b/pintos-progos/tests/vm/mmap-close.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (mmap-close) begin | ||
| 7 | (mmap-close) open "sample.txt" | ||
| 8 | (mmap-close) mmap "sample.txt" | ||
| 9 | (mmap-close) end | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-exit.c b/pintos-progos/tests/vm/mmap-exit.c new file mode 100644 index 0000000..7a2278a --- /dev/null +++ b/pintos-progos/tests/vm/mmap-exit.c | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | /* Executes child-mm-wrt and verifies that the writes that should | ||
| 2 | have occurred really did. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/vm/sample.inc" | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | pid_t child; | ||
| 13 | |||
| 14 | /* Make child write file. */ | ||
| 15 | quiet = true; | ||
| 16 | CHECK ((child = exec ("child-mm-wrt")) != -1, "exec \"child-mm-wrt\""); | ||
| 17 | CHECK (wait (child) == 0, "wait for child (should return 0)"); | ||
| 18 | quiet = false; | ||
| 19 | |||
| 20 | /* Check file contents. */ | ||
| 21 | check_file ("sample.txt", sample, sizeof sample); | ||
| 22 | } | ||
diff --git a/pintos-progos/tests/vm/mmap-exit.ck b/pintos-progos/tests/vm/mmap-exit.ck new file mode 100644 index 0000000..457d34a --- /dev/null +++ b/pintos-progos/tests/vm/mmap-exit.ck | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (mmap-exit) begin | ||
| 7 | (child-mm-wrt) begin | ||
| 8 | (child-mm-wrt) create "sample.txt" | ||
| 9 | (child-mm-wrt) open "sample.txt" | ||
| 10 | (child-mm-wrt) mmap "sample.txt" | ||
| 11 | (child-mm-wrt) end | ||
| 12 | (mmap-exit) open "sample.txt" for verification | ||
| 13 | (mmap-exit) verified contents of "sample.txt" | ||
| 14 | (mmap-exit) close "sample.txt" | ||
| 15 | (mmap-exit) end | ||
| 16 | EOF | ||
| 17 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-inherit.c b/pintos-progos/tests/vm/mmap-inherit.c new file mode 100644 index 0000000..7fa9607 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-inherit.c | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | /* Maps a file into memory and runs child-inherit to verify that | ||
| 2 | mappings are not inherited. */ | ||
| 3 | |||
| 4 | #include <string.h> | ||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/vm/sample.inc" | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | void | ||
| 11 | test_main (void) | ||
| 12 | { | ||
| 13 | char *actual = (char *) 0x54321000; | ||
| 14 | int handle; | ||
| 15 | pid_t child; | ||
| 16 | |||
| 17 | /* Open file, map, verify data. */ | ||
| 18 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 19 | CHECK (mmap (handle, actual) != MAP_FAILED, "mmap \"sample.txt\""); | ||
| 20 | if (memcmp (actual, sample, strlen (sample))) | ||
| 21 | fail ("read of mmap'd file reported bad data"); | ||
| 22 | |||
| 23 | /* Spawn child and wait. */ | ||
| 24 | CHECK ((child = exec ("child-inherit")) != -1, "exec \"child-inherit\""); | ||
| 25 | quiet = true; | ||
| 26 | CHECK (wait (child) == -1, "wait for child (should return -1)"); | ||
| 27 | quiet = false; | ||
| 28 | |||
| 29 | /* Verify data again. */ | ||
| 30 | CHECK (!memcmp (actual, sample, strlen (sample)), | ||
| 31 | "checking that mmap'd file still has same data"); | ||
| 32 | } | ||
diff --git a/pintos-progos/tests/vm/mmap-inherit.ck b/pintos-progos/tests/vm/mmap-inherit.ck new file mode 100644 index 0000000..c54638a --- /dev/null +++ b/pintos-progos/tests/vm/mmap-inherit.ck | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']); | ||
| 6 | (mmap-inherit) begin | ||
| 7 | (mmap-inherit) open "sample.txt" | ||
| 8 | (mmap-inherit) mmap "sample.txt" | ||
| 9 | (mmap-inherit) exec "child-inherit" | ||
| 10 | (child-inherit) begin | ||
| 11 | child-inherit: exit(-1) | ||
| 12 | (mmap-inherit) checking that mmap'd file still has same data | ||
| 13 | (mmap-inherit) end | ||
| 14 | mmap-inherit: exit(0) | ||
| 15 | EOF | ||
| 16 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-lazy-seq.c b/pintos-progos/tests/vm/mmap-lazy-seq.c new file mode 100644 index 0000000..b8f07bd --- /dev/null +++ b/pintos-progos/tests/vm/mmap-lazy-seq.c | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | /* Create a large file, and mmap it several times, writing to | ||
| 2 | different pages. Then unmaps the file, and reads the data back | ||
| 3 | to verify */ | ||
| 4 | |||
| 5 | #include <string.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | #include "tests/vm/sample.inc" | ||
| 8 | #include "tests/lib.h" | ||
| 9 | #include "tests/main.h" | ||
| 10 | |||
| 11 | /* Offset needs to be larger or equal to page size */ | ||
| 12 | #define OFFSET(i) (8192*(i)) | ||
| 13 | /* Number of times file is mmapped */ | ||
| 14 | #define N (8) | ||
| 15 | /* Size of file */ | ||
| 16 | #define FILE_SIZE (1024*1024) | ||
| 17 | /* Address for mmap */ | ||
| 18 | #define ACTUAL(i) ((void *) (0x10000000 + (i)*FILE_SIZE)) | ||
| 19 | |||
| 20 | |||
| 21 | void | ||
| 22 | test_main (void) | ||
| 23 | { | ||
| 24 | int i; | ||
| 25 | int handle; | ||
| 26 | mapid_t map[N]; | ||
| 27 | char buf[1024]; | ||
| 28 | /* create file */ | ||
| 29 | CHECK (create ("sample.txt", FILE_SIZE), "create \"sample.txt\""); | ||
| 30 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 31 | /* mmap */ | ||
| 32 | for (i = 0; i < N; i++) { | ||
| 33 | CHECK ((map[i] = mmap (handle, ACTUAL(i))) != MAP_FAILED, "mmap \"sample.txt\""); | ||
| 34 | } | ||
| 35 | /* write */ | ||
| 36 | for (i = 0; i < N; i++) { | ||
| 37 | memcpy (buf, ACTUAL(i)+OFFSET(i+N), 1024); /* not checked */ | ||
| 38 | memcpy (ACTUAL(i)+OFFSET(i), sample, strlen (sample)); | ||
| 39 | } | ||
| 40 | /* munmap */ | ||
| 41 | for (i = 0; i < N; i++) { | ||
| 42 | munmap (map[i]); | ||
| 43 | } | ||
| 44 | /* Read back via read(). */ | ||
| 45 | for (i = 0; i < N; i++) { | ||
| 46 | seek (handle, OFFSET(i)); | ||
| 47 | read (handle, buf, strlen (sample)); | ||
| 48 | CHECK (!memcmp (buf, sample, strlen (sample)), | ||
| 49 | "compare read data against written data"); | ||
| 50 | } | ||
| 51 | close (handle); | ||
| 52 | } | ||
diff --git a/pintos-progos/tests/vm/mmap-lazy-seq.ck b/pintos-progos/tests/vm/mmap-lazy-seq.ck new file mode 100644 index 0000000..dd0e240 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-lazy-seq.ck | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (mmap-lazy-seq) begin | ||
| 7 | (mmap-lazy-seq) create "sample.txt" | ||
| 8 | (mmap-lazy-seq) open "sample.txt" | ||
| 9 | (mmap-lazy-seq) mmap "sample.txt" | ||
| 10 | (mmap-lazy-seq) mmap "sample.txt" | ||
| 11 | (mmap-lazy-seq) mmap "sample.txt" | ||
| 12 | (mmap-lazy-seq) mmap "sample.txt" | ||
| 13 | (mmap-lazy-seq) mmap "sample.txt" | ||
| 14 | (mmap-lazy-seq) mmap "sample.txt" | ||
| 15 | (mmap-lazy-seq) mmap "sample.txt" | ||
| 16 | (mmap-lazy-seq) mmap "sample.txt" | ||
| 17 | (mmap-lazy-seq) compare read data against written data | ||
| 18 | (mmap-lazy-seq) compare read data against written data | ||
| 19 | (mmap-lazy-seq) compare read data against written data | ||
| 20 | (mmap-lazy-seq) compare read data against written data | ||
| 21 | (mmap-lazy-seq) compare read data against written data | ||
| 22 | (mmap-lazy-seq) compare read data against written data | ||
| 23 | (mmap-lazy-seq) compare read data against written data | ||
| 24 | (mmap-lazy-seq) compare read data against written data | ||
| 25 | (mmap-lazy-seq) end | ||
| 26 | EOF | ||
| 27 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-misalign.c b/pintos-progos/tests/vm/mmap-misalign.c new file mode 100644 index 0000000..34141a9 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-misalign.c | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | /* Verifies that misaligned memory mappings are disallowed. */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | int handle; | ||
| 11 | |||
| 12 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 13 | CHECK (mmap (handle, (void *) 0x10001234) == MAP_FAILED, | ||
| 14 | "try to mmap at misaligned address"); | ||
| 15 | } | ||
| 16 | |||
diff --git a/pintos-progos/tests/vm/mmap-misalign.ck b/pintos-progos/tests/vm/mmap-misalign.ck new file mode 100644 index 0000000..145a2e8 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-misalign.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (mmap-misalign) begin | ||
| 7 | (mmap-misalign) open "sample.txt" | ||
| 8 | (mmap-misalign) try to mmap at misaligned address | ||
| 9 | (mmap-misalign) end | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-null.c b/pintos-progos/tests/vm/mmap-null.c new file mode 100644 index 0000000..f8ef075 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-null.c | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | /* Verifies that memory mappings at address 0 are disallowed. */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | int handle; | ||
| 11 | |||
| 12 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 13 | CHECK (mmap (handle, NULL) == MAP_FAILED, "try to mmap at address 0"); | ||
| 14 | } | ||
| 15 | |||
diff --git a/pintos-progos/tests/vm/mmap-null.ck b/pintos-progos/tests/vm/mmap-null.ck new file mode 100644 index 0000000..aacdd65 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-null.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (mmap-null) begin | ||
| 7 | (mmap-null) open "sample.txt" | ||
| 8 | (mmap-null) try to mmap at address 0 | ||
| 9 | (mmap-null) end | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-over-code.c b/pintos-progos/tests/vm/mmap-over-code.c new file mode 100644 index 0000000..d3619a3 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-over-code.c | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | /* Verifies that mapping over the code segment is disallowed. */ | ||
| 2 | |||
| 3 | #include <stdint.h> | ||
| 4 | #include <round.h> | ||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | uintptr_t test_main_page = ROUND_DOWN ((uintptr_t) test_main, 4096); | ||
| 13 | int handle; | ||
| 14 | |||
| 15 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 16 | CHECK (mmap (handle, (void *) test_main_page) == MAP_FAILED, | ||
| 17 | "try to mmap over code segment"); | ||
| 18 | } | ||
| 19 | |||
diff --git a/pintos-progos/tests/vm/mmap-over-code.ck b/pintos-progos/tests/vm/mmap-over-code.ck new file mode 100644 index 0000000..b5b23c7 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-over-code.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (mmap-over-code) begin | ||
| 7 | (mmap-over-code) open "sample.txt" | ||
| 8 | (mmap-over-code) try to mmap over code segment | ||
| 9 | (mmap-over-code) end | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-over-data.c b/pintos-progos/tests/vm/mmap-over-data.c new file mode 100644 index 0000000..9ea5d49 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-over-data.c | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | /* Verifies that mapping over the data segment is disallowed. */ | ||
| 2 | |||
| 3 | #include <stdint.h> | ||
| 4 | #include <round.h> | ||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | static char x; | ||
| 10 | |||
| 11 | void | ||
| 12 | test_main (void) | ||
| 13 | { | ||
| 14 | uintptr_t x_page = ROUND_DOWN ((uintptr_t) &x, 4096); | ||
| 15 | int handle; | ||
| 16 | |||
| 17 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 18 | CHECK (mmap (handle, (void *) x_page) == MAP_FAILED, | ||
| 19 | "try to mmap over data segment"); | ||
| 20 | } | ||
| 21 | |||
diff --git a/pintos-progos/tests/vm/mmap-over-data.ck b/pintos-progos/tests/vm/mmap-over-data.ck new file mode 100644 index 0000000..98770cc --- /dev/null +++ b/pintos-progos/tests/vm/mmap-over-data.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (mmap-over-data) begin | ||
| 7 | (mmap-over-data) open "sample.txt" | ||
| 8 | (mmap-over-data) try to mmap over data segment | ||
| 9 | (mmap-over-data) end | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-over-stk.c b/pintos-progos/tests/vm/mmap-over-stk.c new file mode 100644 index 0000000..4e241e8 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-over-stk.c | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | /* Verifies that mapping over the stack segment is disallowed. */ | ||
| 2 | |||
| 3 | #include <stdint.h> | ||
| 4 | #include <round.h> | ||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | int handle; | ||
| 13 | uintptr_t handle_page = ROUND_DOWN ((uintptr_t) &handle, 4096); | ||
| 14 | |||
| 15 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 16 | CHECK (mmap (handle, (void *) handle_page) == MAP_FAILED, | ||
| 17 | "try to mmap over stack segment"); | ||
| 18 | } | ||
| 19 | |||
diff --git a/pintos-progos/tests/vm/mmap-over-stk.ck b/pintos-progos/tests/vm/mmap-over-stk.ck new file mode 100644 index 0000000..e6880cf --- /dev/null +++ b/pintos-progos/tests/vm/mmap-over-stk.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (mmap-over-stk) begin | ||
| 7 | (mmap-over-stk) open "sample.txt" | ||
| 8 | (mmap-over-stk) try to mmap over stack segment | ||
| 9 | (mmap-over-stk) end | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-overlap.c b/pintos-progos/tests/vm/mmap-overlap.c new file mode 100644 index 0000000..668ae5f --- /dev/null +++ b/pintos-progos/tests/vm/mmap-overlap.c | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | /* Verifies that overlapping memory mappings are disallowed. */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/vm/sample.inc" | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | char *start = (char *) 0x10000000; | ||
| 12 | int fd[2]; | ||
| 13 | |||
| 14 | CHECK ((fd[0] = open ("zeros")) > 1, "open \"zeros\" once"); | ||
| 15 | CHECK (mmap (fd[0], start) != MAP_FAILED, "mmap \"zeros\""); | ||
| 16 | CHECK ((fd[1] = open ("zeros")) > 1 && fd[0] != fd[1], | ||
| 17 | "open \"zeros\" again"); | ||
| 18 | CHECK (mmap (fd[1], start + 4096) == MAP_FAILED, | ||
| 19 | "try to mmap \"zeros\" again"); | ||
| 20 | } | ||
diff --git a/pintos-progos/tests/vm/mmap-overlap.ck b/pintos-progos/tests/vm/mmap-overlap.ck new file mode 100644 index 0000000..f13801e --- /dev/null +++ b/pintos-progos/tests/vm/mmap-overlap.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (mmap-overlap) begin | ||
| 7 | (mmap-overlap) open "zeros" once | ||
| 8 | (mmap-overlap) mmap "zeros" | ||
| 9 | (mmap-overlap) open "zeros" again | ||
| 10 | (mmap-overlap) try to mmap "zeros" again | ||
| 11 | (mmap-overlap) end | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-read.c b/pintos-progos/tests/vm/mmap-read.c new file mode 100644 index 0000000..c0f23a1 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-read.c | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | /* Uses a memory mapping to read a file. */ | ||
| 2 | |||
| 3 | #include <string.h> | ||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/vm/sample.inc" | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | char *actual = (char *) 0x10000000; | ||
| 13 | int handle; | ||
| 14 | mapid_t map; | ||
| 15 | size_t i; | ||
| 16 | |||
| 17 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 18 | CHECK ((map = mmap (handle, actual)) != MAP_FAILED, "mmap \"sample.txt\""); | ||
| 19 | |||
| 20 | /* Check that data is correct. */ | ||
| 21 | if (memcmp (actual, sample, strlen (sample))) | ||
| 22 | fail ("read of mmap'd file reported bad data"); | ||
| 23 | |||
| 24 | /* Verify that data is followed by zeros. */ | ||
| 25 | for (i = strlen (sample); i < 4096; i++) | ||
| 26 | if (actual[i] != 0) | ||
| 27 | fail ("byte %zu of mmap'd region has value %02hhx (should be 0)", | ||
| 28 | i, actual[i]); | ||
| 29 | |||
| 30 | munmap (map); | ||
| 31 | close (handle); | ||
| 32 | } | ||
diff --git a/pintos-progos/tests/vm/mmap-read.ck b/pintos-progos/tests/vm/mmap-read.ck new file mode 100644 index 0000000..95ab790 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-read.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (mmap-read) begin | ||
| 7 | (mmap-read) open "sample.txt" | ||
| 8 | (mmap-read) mmap "sample.txt" | ||
| 9 | (mmap-read) end | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-remove.c b/pintos-progos/tests/vm/mmap-remove.c new file mode 100644 index 0000000..5f7444d --- /dev/null +++ b/pintos-progos/tests/vm/mmap-remove.c | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | /* Deletes and closes file that is mapped into memory | ||
| 2 | and verifies that it can still be read through the mapping. */ | ||
| 3 | |||
| 4 | #include <string.h> | ||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/vm/sample.inc" | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | void | ||
| 11 | test_main (void) | ||
| 12 | { | ||
| 13 | char *actual = (char *) 0x10000000; | ||
| 14 | int handle; | ||
| 15 | mapid_t map; | ||
| 16 | size_t i; | ||
| 17 | |||
| 18 | /* Map file. */ | ||
| 19 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 20 | CHECK ((map = mmap (handle, actual)) != MAP_FAILED, "mmap \"sample.txt\""); | ||
| 21 | |||
| 22 | /* Close file and delete it. */ | ||
| 23 | close (handle); | ||
| 24 | CHECK (remove ("sample.txt"), "remove \"sample.txt\""); | ||
| 25 | CHECK (open ("sample.txt") == -1, "try to open \"sample.txt\""); | ||
| 26 | |||
| 27 | /* Create a new file in hopes of overwriting data from the old | ||
| 28 | one, in case the file system has incorrectly freed the | ||
| 29 | file's data. */ | ||
| 30 | CHECK (create ("another", 4096 * 10), "create \"another\""); | ||
| 31 | |||
| 32 | /* Check that mapped data is correct. */ | ||
| 33 | if (memcmp (actual, sample, strlen (sample))) | ||
| 34 | fail ("read of mmap'd file reported bad data"); | ||
| 35 | |||
| 36 | /* Verify that data is followed by zeros. */ | ||
| 37 | for (i = strlen (sample); i < 4096; i++) | ||
| 38 | if (actual[i] != 0) | ||
| 39 | fail ("byte %zu of mmap'd region has value %02hhx (should be 0)", | ||
| 40 | i, actual[i]); | ||
| 41 | |||
| 42 | munmap (map); | ||
| 43 | } | ||
diff --git a/pintos-progos/tests/vm/mmap-remove.ck b/pintos-progos/tests/vm/mmap-remove.ck new file mode 100644 index 0000000..d3cc938 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-remove.ck | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (mmap-remove) begin | ||
| 7 | (mmap-remove) open "sample.txt" | ||
| 8 | (mmap-remove) mmap "sample.txt" | ||
| 9 | (mmap-remove) remove "sample.txt" | ||
| 10 | (mmap-remove) try to open "sample.txt" | ||
| 11 | (mmap-remove) create "another" | ||
| 12 | (mmap-remove) end | ||
| 13 | EOF | ||
| 14 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-shuffle.c b/pintos-progos/tests/vm/mmap-shuffle.c new file mode 100644 index 0000000..29921ad --- /dev/null +++ b/pintos-progos/tests/vm/mmap-shuffle.c | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | /* Creates a 128 kB file and repeatedly shuffles data in it | ||
| 2 | through a memory mapping. */ | ||
| 3 | |||
| 4 | #include <stdio.h> | ||
| 5 | #include <string.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | #include "tests/arc4.h" | ||
| 8 | #include "tests/cksum.h" | ||
| 9 | #include "tests/lib.h" | ||
| 10 | #include "tests/main.h" | ||
| 11 | |||
| 12 | #define SIZE (128 * 1024) | ||
| 13 | |||
| 14 | static char *buf = (char *) 0x10000000; | ||
| 15 | |||
| 16 | void | ||
| 17 | test_main (void) | ||
| 18 | { | ||
| 19 | size_t i; | ||
| 20 | int handle; | ||
| 21 | |||
| 22 | /* Create file, mmap. */ | ||
| 23 | CHECK (create ("buffer", SIZE), "create \"buffer\""); | ||
| 24 | CHECK ((handle = open ("buffer")) > 1, "open \"buffer\""); | ||
| 25 | CHECK (mmap (handle, buf) != MAP_FAILED, "mmap \"buffer\""); | ||
| 26 | |||
| 27 | /* Initialize. */ | ||
| 28 | for (i = 0; i < SIZE; i++) | ||
| 29 | buf[i] = i * 257; | ||
| 30 | msg ("init: cksum=%lu", cksum (buf, SIZE)); | ||
| 31 | |||
| 32 | /* Shuffle repeatedly. */ | ||
| 33 | for (i = 0; i < 10; i++) | ||
| 34 | { | ||
| 35 | shuffle (buf, SIZE, 1); | ||
| 36 | msg ("shuffle %zu: cksum=%lu", i, cksum (buf, SIZE)); | ||
| 37 | } | ||
| 38 | } | ||
diff --git a/pintos-progos/tests/vm/mmap-shuffle.ck b/pintos-progos/tests/vm/mmap-shuffle.ck new file mode 100644 index 0000000..c158301 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-shuffle.ck | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::cksum; | ||
| 6 | use tests::lib; | ||
| 7 | |||
| 8 | my ($init, @shuffle); | ||
| 9 | if (1) { | ||
| 10 | # Use precalculated values. | ||
| 11 | $init = 3115322833; | ||
| 12 | @shuffle = (1691062564, 1973575879, 1647619479, 96566261, 3885786467, | ||
| 13 | 3022003332, 3614934266, 2704001777, 735775156, 1864109763); | ||
| 14 | } else { | ||
| 15 | # Recalculate values. | ||
| 16 | my ($buf) = ""; | ||
| 17 | for my $i (0...128 * 1024 - 1) { | ||
| 18 | $buf .= chr (($i * 257) & 0xff); | ||
| 19 | } | ||
| 20 | $init = cksum ($buf); | ||
| 21 | |||
| 22 | random_init (0); | ||
| 23 | for my $i (1...10) { | ||
| 24 | $buf = shuffle ($buf, length ($buf), 1); | ||
| 25 | push (@shuffle, cksum ($buf)); | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | check_expected (IGNORE_EXIT_CODES => 1, [<<EOF]); | ||
| 30 | (mmap-shuffle) begin | ||
| 31 | (mmap-shuffle) create "buffer" | ||
| 32 | (mmap-shuffle) open "buffer" | ||
| 33 | (mmap-shuffle) mmap "buffer" | ||
| 34 | (mmap-shuffle) init: cksum=$init | ||
| 35 | (mmap-shuffle) shuffle 0: cksum=$shuffle[0] | ||
| 36 | (mmap-shuffle) shuffle 1: cksum=$shuffle[1] | ||
| 37 | (mmap-shuffle) shuffle 2: cksum=$shuffle[2] | ||
| 38 | (mmap-shuffle) shuffle 3: cksum=$shuffle[3] | ||
| 39 | (mmap-shuffle) shuffle 4: cksum=$shuffle[4] | ||
| 40 | (mmap-shuffle) shuffle 5: cksum=$shuffle[5] | ||
| 41 | (mmap-shuffle) shuffle 6: cksum=$shuffle[6] | ||
| 42 | (mmap-shuffle) shuffle 7: cksum=$shuffle[7] | ||
| 43 | (mmap-shuffle) shuffle 8: cksum=$shuffle[8] | ||
| 44 | (mmap-shuffle) shuffle 9: cksum=$shuffle[9] | ||
| 45 | (mmap-shuffle) end | ||
| 46 | EOF | ||
| 47 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-twice.c b/pintos-progos/tests/vm/mmap-twice.c new file mode 100644 index 0000000..d277a37 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-twice.c | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | /* Maps the same file into memory twice and verifies that the | ||
| 2 | same data is readable in both. */ | ||
| 3 | |||
| 4 | #include <string.h> | ||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/vm/sample.inc" | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | void | ||
| 11 | test_main (void) | ||
| 12 | { | ||
| 13 | char *actual[2] = {(char *) 0x10000000, (char *) 0x20000000}; | ||
| 14 | size_t i; | ||
| 15 | int handle[2]; | ||
| 16 | |||
| 17 | for (i = 0; i < 2; i++) | ||
| 18 | { | ||
| 19 | CHECK ((handle[i] = open ("sample.txt")) > 1, | ||
| 20 | "open \"sample.txt\" #%zu", i); | ||
| 21 | CHECK (mmap (handle[i], actual[i]) != MAP_FAILED, | ||
| 22 | "mmap \"sample.txt\" #%zu at %p", i, (void *) actual[i]); | ||
| 23 | } | ||
| 24 | |||
| 25 | for (i = 0; i < 2; i++) | ||
| 26 | CHECK (!memcmp (actual[i], sample, strlen (sample)), | ||
| 27 | "compare mmap'd file %zu against data", i); | ||
| 28 | } | ||
diff --git a/pintos-progos/tests/vm/mmap-twice.ck b/pintos-progos/tests/vm/mmap-twice.ck new file mode 100644 index 0000000..05e9724 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-twice.ck | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (mmap-twice) begin | ||
| 7 | (mmap-twice) open "sample.txt" #0 | ||
| 8 | (mmap-twice) mmap "sample.txt" #0 at 0x10000000 | ||
| 9 | (mmap-twice) open "sample.txt" #1 | ||
| 10 | (mmap-twice) mmap "sample.txt" #1 at 0x20000000 | ||
| 11 | (mmap-twice) compare mmap'd file 0 against data | ||
| 12 | (mmap-twice) compare mmap'd file 1 against data | ||
| 13 | (mmap-twice) end | ||
| 14 | EOF | ||
| 15 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-unmap.c b/pintos-progos/tests/vm/mmap-unmap.c new file mode 100644 index 0000000..d35a79e --- /dev/null +++ b/pintos-progos/tests/vm/mmap-unmap.c | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | /* Maps and unmaps a file and verifies that the mapped region is | ||
| 2 | inaccessible afterward. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/vm/sample.inc" | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | #define ACTUAL ((void *) 0x10000000) | ||
| 10 | |||
| 11 | void | ||
| 12 | test_main (void) | ||
| 13 | { | ||
| 14 | int handle; | ||
| 15 | mapid_t map; | ||
| 16 | |||
| 17 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 18 | CHECK ((map = mmap (handle, ACTUAL)) != MAP_FAILED, "mmap \"sample.txt\""); | ||
| 19 | |||
| 20 | munmap (map); | ||
| 21 | |||
| 22 | fail ("unmapped memory is readable (%d)", *(int *) ACTUAL); | ||
| 23 | } | ||
diff --git a/pintos-progos/tests/vm/mmap-unmap.ck b/pintos-progos/tests/vm/mmap-unmap.ck new file mode 100644 index 0000000..119658c --- /dev/null +++ b/pintos-progos/tests/vm/mmap-unmap.ck | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::vm::process_death; | ||
| 6 | |||
| 7 | check_process_death ('mmap-unmap'); | ||
diff --git a/pintos-progos/tests/vm/mmap-write.c b/pintos-progos/tests/vm/mmap-write.c new file mode 100644 index 0000000..46e8043 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-write.c | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | /* Writes to a file through a mapping, and unmaps the file, | ||
| 2 | then reads the data in the file back using the read system | ||
| 3 | call to verify. */ | ||
| 4 | |||
| 5 | #include <string.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | #include "tests/vm/sample.inc" | ||
| 8 | #include "tests/lib.h" | ||
| 9 | #include "tests/main.h" | ||
| 10 | |||
| 11 | #define ACTUAL ((void *) 0x10000000) | ||
| 12 | |||
| 13 | void | ||
| 14 | test_main (void) | ||
| 15 | { | ||
| 16 | int handle; | ||
| 17 | mapid_t map; | ||
| 18 | char buf[1024]; | ||
| 19 | |||
| 20 | /* Write file via mmap. */ | ||
| 21 | CHECK (create ("sample.txt", strlen (sample)), "create \"sample.txt\""); | ||
| 22 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 23 | CHECK ((map = mmap (handle, ACTUAL)) != MAP_FAILED, "mmap \"sample.txt\""); | ||
| 24 | memcpy (ACTUAL, sample, strlen (sample)); | ||
| 25 | munmap (map); | ||
| 26 | |||
| 27 | /* Read back via read(). */ | ||
| 28 | read (handle, buf, strlen (sample)); | ||
| 29 | CHECK (!memcmp (buf, sample, strlen (sample)), | ||
| 30 | "compare read data against written data"); | ||
| 31 | close (handle); | ||
| 32 | } | ||
diff --git a/pintos-progos/tests/vm/mmap-write.ck b/pintos-progos/tests/vm/mmap-write.ck new file mode 100644 index 0000000..d2c9cc5 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-write.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (mmap-write) begin | ||
| 7 | (mmap-write) create "sample.txt" | ||
| 8 | (mmap-write) open "sample.txt" | ||
| 9 | (mmap-write) mmap "sample.txt" | ||
| 10 | (mmap-write) compare read data against written data | ||
| 11 | (mmap-write) end | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-zero.c b/pintos-progos/tests/vm/mmap-zero.c new file mode 100644 index 0000000..368b759 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-zero.c | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | /* Tries to map a zero-length file, which may or may not work but | ||
| 2 | should not terminate the process or crash. | ||
| 3 | Then dereferences the address that we tried to map, | ||
| 4 | and the process must be terminated with -1 exit code. */ | ||
| 5 | |||
| 6 | #include <syscall.h> | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | void | ||
| 11 | test_main (void) | ||
| 12 | { | ||
| 13 | char *data = (char *) 0x7f000000; | ||
| 14 | int handle; | ||
| 15 | |||
| 16 | CHECK (create ("empty", 0), "create empty file \"empty\""); | ||
| 17 | CHECK ((handle = open ("empty")) > 1, "open \"empty\""); | ||
| 18 | |||
| 19 | /* Calling mmap() might succeed or fail. We don't care. */ | ||
| 20 | msg ("mmap \"empty\""); | ||
| 21 | mmap (handle, data); | ||
| 22 | |||
| 23 | /* Regardless of whether the call worked, *data should cause | ||
| 24 | the process to be terminated. */ | ||
| 25 | fail ("unmapped memory is readable (%d)", *data); | ||
| 26 | } | ||
| 27 | |||
diff --git a/pintos-progos/tests/vm/mmap-zero.ck b/pintos-progos/tests/vm/mmap-zero.ck new file mode 100644 index 0000000..6033d5d --- /dev/null +++ b/pintos-progos/tests/vm/mmap-zero.ck | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']); | ||
| 6 | (mmap-zero) begin | ||
| 7 | (mmap-zero) create empty file "empty" | ||
| 8 | (mmap-zero) open "empty" | ||
| 9 | (mmap-zero) mmap "empty" | ||
| 10 | mmap-zero: exit(-1) | ||
| 11 | EOF | ||
| 12 | pass; | ||
diff --git a/pintos-progos/tests/vm/page-linear.c b/pintos-progos/tests/vm/page-linear.c new file mode 100644 index 0000000..652a47b --- /dev/null +++ b/pintos-progos/tests/vm/page-linear.c | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | /* Encrypts, then decrypts, 2 MB of memory and verifies that the | ||
| 2 | values are as they should be. */ | ||
| 3 | |||
| 4 | #include <string.h> | ||
| 5 | #include "tests/arc4.h" | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | #define SIZE (2 * 1024 * 1024) | ||
| 10 | |||
| 11 | static char buf[SIZE]; | ||
| 12 | |||
| 13 | void | ||
| 14 | test_main (void) | ||
| 15 | { | ||
| 16 | struct arc4 arc4; | ||
| 17 | size_t i; | ||
| 18 | |||
| 19 | /* Initialize to 0x5a. */ | ||
| 20 | msg ("initialize"); | ||
| 21 | memset (buf, 0x5a, sizeof buf); | ||
| 22 | |||
| 23 | /* Check that it's all 0x5a. */ | ||
| 24 | msg ("read pass"); | ||
| 25 | for (i = 0; i < SIZE; i++) | ||
| 26 | if (buf[i] != 0x5a) | ||
| 27 | fail ("byte %zu != 0x5a", i); | ||
| 28 | |||
| 29 | /* Encrypt zeros. */ | ||
| 30 | msg ("read/modify/write pass one"); | ||
| 31 | arc4_init (&arc4, "foobar", 6); | ||
| 32 | arc4_crypt (&arc4, buf, SIZE); | ||
| 33 | |||
| 34 | /* Decrypt back to zeros. */ | ||
| 35 | msg ("read/modify/write pass two"); | ||
| 36 | arc4_init (&arc4, "foobar", 6); | ||
| 37 | arc4_crypt (&arc4, buf, SIZE); | ||
| 38 | |||
| 39 | /* Check that it's all 0x5a. */ | ||
| 40 | msg ("read pass"); | ||
| 41 | for (i = 0; i < SIZE; i++) | ||
| 42 | if (buf[i] != 0x5a) | ||
| 43 | fail ("byte %zu != 0x5a", i); | ||
| 44 | } | ||
diff --git a/pintos-progos/tests/vm/page-linear.ck b/pintos-progos/tests/vm/page-linear.ck new file mode 100644 index 0000000..dcbc884 --- /dev/null +++ b/pintos-progos/tests/vm/page-linear.ck | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (page-linear) begin | ||
| 7 | (page-linear) initialize | ||
| 8 | (page-linear) read pass | ||
| 9 | (page-linear) read/modify/write pass one | ||
| 10 | (page-linear) read/modify/write pass two | ||
| 11 | (page-linear) read pass | ||
| 12 | (page-linear) end | ||
| 13 | EOF | ||
| 14 | pass; | ||
diff --git a/pintos-progos/tests/vm/page-merge-mm.c b/pintos-progos/tests/vm/page-merge-mm.c new file mode 100644 index 0000000..908c71c --- /dev/null +++ b/pintos-progos/tests/vm/page-merge-mm.c | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | #include "tests/main.h" | ||
| 2 | #include "tests/vm/parallel-merge.h" | ||
| 3 | |||
| 4 | void | ||
| 5 | test_main (void) | ||
| 6 | { | ||
| 7 | parallel_merge ("child-qsort-mm", 80); | ||
| 8 | } | ||
diff --git a/pintos-progos/tests/vm/page-merge-mm.ck b/pintos-progos/tests/vm/page-merge-mm.ck new file mode 100644 index 0000000..74fa980 --- /dev/null +++ b/pintos-progos/tests/vm/page-merge-mm.ck | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (page-merge-mm) begin | ||
| 7 | (page-merge-mm) init | ||
| 8 | (page-merge-mm) sort chunk 0 | ||
| 9 | (page-merge-mm) sort chunk 1 | ||
| 10 | (page-merge-mm) sort chunk 2 | ||
| 11 | (page-merge-mm) sort chunk 3 | ||
| 12 | (page-merge-mm) sort chunk 4 | ||
| 13 | (page-merge-mm) sort chunk 5 | ||
| 14 | (page-merge-mm) sort chunk 6 | ||
| 15 | (page-merge-mm) sort chunk 7 | ||
| 16 | (page-merge-mm) wait for child 0 | ||
| 17 | (page-merge-mm) wait for child 1 | ||
| 18 | (page-merge-mm) wait for child 2 | ||
| 19 | (page-merge-mm) wait for child 3 | ||
| 20 | (page-merge-mm) wait for child 4 | ||
| 21 | (page-merge-mm) wait for child 5 | ||
| 22 | (page-merge-mm) wait for child 6 | ||
| 23 | (page-merge-mm) wait for child 7 | ||
| 24 | (page-merge-mm) merge | ||
| 25 | (page-merge-mm) verify | ||
| 26 | (page-merge-mm) success, buf_idx=1,048,576 | ||
| 27 | (page-merge-mm) end | ||
| 28 | EOF | ||
| 29 | pass; | ||
diff --git a/pintos-progos/tests/vm/page-merge-par.c b/pintos-progos/tests/vm/page-merge-par.c new file mode 100644 index 0000000..e7e1609 --- /dev/null +++ b/pintos-progos/tests/vm/page-merge-par.c | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | #include "tests/main.h" | ||
| 2 | #include "tests/vm/parallel-merge.h" | ||
| 3 | |||
| 4 | void | ||
| 5 | test_main (void) | ||
| 6 | { | ||
| 7 | parallel_merge ("child-sort", 123); | ||
| 8 | } | ||
diff --git a/pintos-progos/tests/vm/page-merge-par.ck b/pintos-progos/tests/vm/page-merge-par.ck new file mode 100644 index 0000000..31f8aa7 --- /dev/null +++ b/pintos-progos/tests/vm/page-merge-par.ck | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (page-merge-par) begin | ||
| 7 | (page-merge-par) init | ||
| 8 | (page-merge-par) sort chunk 0 | ||
| 9 | (page-merge-par) sort chunk 1 | ||
| 10 | (page-merge-par) sort chunk 2 | ||
| 11 | (page-merge-par) sort chunk 3 | ||
| 12 | (page-merge-par) sort chunk 4 | ||
| 13 | (page-merge-par) sort chunk 5 | ||
| 14 | (page-merge-par) sort chunk 6 | ||
| 15 | (page-merge-par) sort chunk 7 | ||
| 16 | (page-merge-par) wait for child 0 | ||
| 17 | (page-merge-par) wait for child 1 | ||
| 18 | (page-merge-par) wait for child 2 | ||
| 19 | (page-merge-par) wait for child 3 | ||
| 20 | (page-merge-par) wait for child 4 | ||
| 21 | (page-merge-par) wait for child 5 | ||
| 22 | (page-merge-par) wait for child 6 | ||
| 23 | (page-merge-par) wait for child 7 | ||
| 24 | (page-merge-par) merge | ||
| 25 | (page-merge-par) verify | ||
| 26 | (page-merge-par) success, buf_idx=1,048,576 | ||
| 27 | (page-merge-par) end | ||
| 28 | EOF | ||
| 29 | pass; | ||
diff --git a/pintos-progos/tests/vm/page-merge-seq.c b/pintos-progos/tests/vm/page-merge-seq.c new file mode 100644 index 0000000..12e3880 --- /dev/null +++ b/pintos-progos/tests/vm/page-merge-seq.c | |||
| @@ -0,0 +1,137 @@ | |||
| 1 | /* Generates about 1 MB of random data that is then divided into | ||
| 2 | 16 chunks. A separate subprocess sorts each chunk in | ||
| 3 | sequence. Then we merge the chunks and verify that the result | ||
| 4 | is what it should be. */ | ||
| 5 | |||
| 6 | #include <syscall.h> | ||
| 7 | #include "tests/arc4.h" | ||
| 8 | #include "tests/lib.h" | ||
| 9 | #include "tests/main.h" | ||
| 10 | |||
| 11 | /* This is the max file size for an older version of the Pintos | ||
| 12 | file system that had 126 direct blocks each pointing to a | ||
| 13 | single disk sector. We could raise it now. */ | ||
| 14 | #define CHUNK_SIZE (126 * 512) | ||
| 15 | #define CHUNK_CNT 16 /* Number of chunks. */ | ||
| 16 | #define DATA_SIZE (CHUNK_CNT * CHUNK_SIZE) /* Buffer size. */ | ||
| 17 | |||
| 18 | unsigned char buf1[DATA_SIZE], buf2[DATA_SIZE]; | ||
| 19 | size_t histogram[256]; | ||
| 20 | |||
| 21 | /* Initialize buf1 with random data, | ||
| 22 | then count the number of instances of each value within it. */ | ||
| 23 | static void | ||
| 24 | init (void) | ||
| 25 | { | ||
| 26 | struct arc4 arc4; | ||
| 27 | size_t i; | ||
| 28 | |||
| 29 | msg ("init"); | ||
| 30 | |||
| 31 | arc4_init (&arc4, "foobar", 6); | ||
| 32 | arc4_crypt (&arc4, buf1, sizeof buf1); | ||
| 33 | for (i = 0; i < sizeof buf1; i++) | ||
| 34 | histogram[buf1[i]]++; | ||
| 35 | } | ||
| 36 | |||
| 37 | /* Sort each chunk of buf1 using a subprocess. */ | ||
| 38 | static void | ||
| 39 | sort_chunks (void) | ||
| 40 | { | ||
| 41 | size_t i; | ||
| 42 | |||
| 43 | create ("buffer", CHUNK_SIZE); | ||
| 44 | for (i = 0; i < CHUNK_CNT; i++) | ||
| 45 | { | ||
| 46 | pid_t child; | ||
| 47 | int handle; | ||
| 48 | |||
| 49 | msg ("sort chunk %zu", i); | ||
| 50 | |||
| 51 | /* Write this chunk to a file. */ | ||
| 52 | quiet = true; | ||
| 53 | CHECK ((handle = open ("buffer")) > 1, "open \"buffer\""); | ||
| 54 | write (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE); | ||
| 55 | close (handle); | ||
| 56 | |||
| 57 | /* Sort with subprocess. */ | ||
| 58 | CHECK ((child = exec ("child-sort buffer")) != -1, | ||
| 59 | "exec \"child-sort buffer\""); | ||
| 60 | CHECK (wait (child) == 123, "wait for child-sort"); | ||
| 61 | |||
| 62 | /* Read chunk back from file. */ | ||
| 63 | CHECK ((handle = open ("buffer")) > 1, "open \"buffer\""); | ||
| 64 | read (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE); | ||
| 65 | close (handle); | ||
| 66 | |||
| 67 | quiet = false; | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | /* Merge the sorted chunks in buf1 into a fully sorted buf2. */ | ||
| 72 | static void | ||
| 73 | merge (void) | ||
| 74 | { | ||
| 75 | unsigned char *mp[CHUNK_CNT]; | ||
| 76 | size_t mp_left; | ||
| 77 | unsigned char *op; | ||
| 78 | size_t i; | ||
| 79 | |||
| 80 | msg ("merge"); | ||
| 81 | |||
| 82 | /* Initialize merge pointers. */ | ||
| 83 | mp_left = CHUNK_CNT; | ||
| 84 | for (i = 0; i < CHUNK_CNT; i++) | ||
| 85 | mp[i] = buf1 + CHUNK_SIZE * i; | ||
| 86 | |||
| 87 | /* Merge. */ | ||
| 88 | op = buf2; | ||
| 89 | while (mp_left > 0) | ||
| 90 | { | ||
| 91 | /* Find smallest value. */ | ||
| 92 | size_t min = 0; | ||
| 93 | for (i = 1; i < mp_left; i++) | ||
| 94 | if (*mp[i] < *mp[min]) | ||
| 95 | min = i; | ||
| 96 | |||
| 97 | /* Append value to buf2. */ | ||
| 98 | *op++ = *mp[min]; | ||
| 99 | |||
| 100 | /* Advance merge pointer. | ||
| 101 | Delete this chunk from the set if it's emptied. */ | ||
| 102 | if ((++mp[min] - buf1) % CHUNK_SIZE == 0) | ||
| 103 | mp[min] = mp[--mp_left]; | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | static void | ||
| 108 | verify (void) | ||
| 109 | { | ||
| 110 | size_t buf_idx; | ||
| 111 | size_t hist_idx; | ||
| 112 | |||
| 113 | msg ("verify"); | ||
| 114 | |||
| 115 | buf_idx = 0; | ||
| 116 | for (hist_idx = 0; hist_idx < sizeof histogram / sizeof *histogram; | ||
| 117 | hist_idx++) | ||
| 118 | { | ||
| 119 | while (histogram[hist_idx]-- > 0) | ||
| 120 | { | ||
| 121 | if (buf2[buf_idx] != hist_idx) | ||
| 122 | fail ("bad value %d in byte %zu", buf2[buf_idx], buf_idx); | ||
| 123 | buf_idx++; | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | msg ("success, buf_idx=%'zu", buf_idx); | ||
| 128 | } | ||
| 129 | |||
| 130 | void | ||
| 131 | test_main (void) | ||
| 132 | { | ||
| 133 | init (); | ||
| 134 | sort_chunks (); | ||
| 135 | merge (); | ||
| 136 | verify (); | ||
| 137 | } | ||
diff --git a/pintos-progos/tests/vm/page-merge-seq.ck b/pintos-progos/tests/vm/page-merge-seq.ck new file mode 100644 index 0000000..d78f69d --- /dev/null +++ b/pintos-progos/tests/vm/page-merge-seq.ck | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (page-merge-seq) begin | ||
| 7 | (page-merge-seq) init | ||
| 8 | (page-merge-seq) sort chunk 0 | ||
| 9 | (page-merge-seq) sort chunk 1 | ||
| 10 | (page-merge-seq) sort chunk 2 | ||
| 11 | (page-merge-seq) sort chunk 3 | ||
| 12 | (page-merge-seq) sort chunk 4 | ||
| 13 | (page-merge-seq) sort chunk 5 | ||
| 14 | (page-merge-seq) sort chunk 6 | ||
| 15 | (page-merge-seq) sort chunk 7 | ||
| 16 | (page-merge-seq) sort chunk 8 | ||
| 17 | (page-merge-seq) sort chunk 9 | ||
| 18 | (page-merge-seq) sort chunk 10 | ||
| 19 | (page-merge-seq) sort chunk 11 | ||
| 20 | (page-merge-seq) sort chunk 12 | ||
| 21 | (page-merge-seq) sort chunk 13 | ||
| 22 | (page-merge-seq) sort chunk 14 | ||
| 23 | (page-merge-seq) sort chunk 15 | ||
| 24 | (page-merge-seq) merge | ||
| 25 | (page-merge-seq) verify | ||
| 26 | (page-merge-seq) success, buf_idx=1,032,192 | ||
| 27 | (page-merge-seq) end | ||
| 28 | EOF | ||
| 29 | pass; | ||
diff --git a/pintos-progos/tests/vm/page-merge-stk.c b/pintos-progos/tests/vm/page-merge-stk.c new file mode 100644 index 0000000..5eb1069 --- /dev/null +++ b/pintos-progos/tests/vm/page-merge-stk.c | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | #include "tests/main.h" | ||
| 2 | #include "tests/vm/parallel-merge.h" | ||
| 3 | |||
| 4 | void | ||
| 5 | test_main (void) | ||
| 6 | { | ||
| 7 | parallel_merge ("child-qsort", 72); | ||
| 8 | } | ||
diff --git a/pintos-progos/tests/vm/page-merge-stk.ck b/pintos-progos/tests/vm/page-merge-stk.ck new file mode 100644 index 0000000..c5bc1ae --- /dev/null +++ b/pintos-progos/tests/vm/page-merge-stk.ck | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (page-merge-stk) begin | ||
| 7 | (page-merge-stk) init | ||
| 8 | (page-merge-stk) sort chunk 0 | ||
| 9 | (page-merge-stk) sort chunk 1 | ||
| 10 | (page-merge-stk) sort chunk 2 | ||
| 11 | (page-merge-stk) sort chunk 3 | ||
| 12 | (page-merge-stk) sort chunk 4 | ||
| 13 | (page-merge-stk) sort chunk 5 | ||
| 14 | (page-merge-stk) sort chunk 6 | ||
| 15 | (page-merge-stk) sort chunk 7 | ||
| 16 | (page-merge-stk) wait for child 0 | ||
| 17 | (page-merge-stk) wait for child 1 | ||
| 18 | (page-merge-stk) wait for child 2 | ||
| 19 | (page-merge-stk) wait for child 3 | ||
| 20 | (page-merge-stk) wait for child 4 | ||
| 21 | (page-merge-stk) wait for child 5 | ||
| 22 | (page-merge-stk) wait for child 6 | ||
| 23 | (page-merge-stk) wait for child 7 | ||
| 24 | (page-merge-stk) merge | ||
| 25 | (page-merge-stk) verify | ||
| 26 | (page-merge-stk) success, buf_idx=1,048,576 | ||
| 27 | (page-merge-stk) end | ||
| 28 | EOF | ||
| 29 | pass; | ||
diff --git a/pintos-progos/tests/vm/page-parallel.c b/pintos-progos/tests/vm/page-parallel.c new file mode 100644 index 0000000..9d619e0 --- /dev/null +++ b/pintos-progos/tests/vm/page-parallel.c | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | /* Runs 4 child-linear processes at once. */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | #define CHILD_CNT 4 | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | pid_t children[CHILD_CNT]; | ||
| 13 | int i; | ||
| 14 | |||
| 15 | for (i = 0; i < CHILD_CNT; i++) | ||
| 16 | CHECK ((children[i] = exec ("child-linear")) != -1, | ||
| 17 | "exec \"child-linear\""); | ||
| 18 | |||
| 19 | for (i = 0; i < CHILD_CNT; i++) | ||
| 20 | CHECK (wait (children[i]) == 0x42, "wait for child %d", i); | ||
| 21 | } | ||
diff --git a/pintos-progos/tests/vm/page-parallel.ck b/pintos-progos/tests/vm/page-parallel.ck new file mode 100644 index 0000000..90c14ef --- /dev/null +++ b/pintos-progos/tests/vm/page-parallel.ck | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (page-parallel) begin | ||
| 7 | (page-parallel) exec "child-linear" | ||
| 8 | (page-parallel) exec "child-linear" | ||
| 9 | (page-parallel) exec "child-linear" | ||
| 10 | (page-parallel) exec "child-linear" | ||
| 11 | (page-parallel) wait for child 0 | ||
| 12 | (page-parallel) wait for child 1 | ||
| 13 | (page-parallel) wait for child 2 | ||
| 14 | (page-parallel) wait for child 3 | ||
| 15 | (page-parallel) end | ||
| 16 | EOF | ||
| 17 | pass; | ||
diff --git a/pintos-progos/tests/vm/page-shuffle.c b/pintos-progos/tests/vm/page-shuffle.c new file mode 100644 index 0000000..095a9da --- /dev/null +++ b/pintos-progos/tests/vm/page-shuffle.c | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | /* Shuffles a 128 kB data buffer 10 times, printing the checksum | ||
| 2 | after each time. */ | ||
| 3 | |||
| 4 | #include <stdbool.h> | ||
| 5 | #include "tests/arc4.h" | ||
| 6 | #include "tests/cksum.h" | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | #define SIZE (128 * 1024) | ||
| 11 | |||
| 12 | static char buf[SIZE]; | ||
| 13 | |||
| 14 | void | ||
| 15 | test_main (void) | ||
| 16 | { | ||
| 17 | size_t i; | ||
| 18 | |||
| 19 | /* Initialize. */ | ||
| 20 | for (i = 0; i < sizeof buf; i++) | ||
| 21 | buf[i] = i * 257; | ||
| 22 | msg ("init: cksum=%lu", cksum (buf, sizeof buf)); | ||
| 23 | |||
| 24 | /* Shuffle repeatedly. */ | ||
| 25 | for (i = 0; i < 10; i++) | ||
| 26 | { | ||
| 27 | shuffle (buf, sizeof buf, 1); | ||
| 28 | msg ("shuffle %zu: cksum=%lu", i, cksum (buf, sizeof buf)); | ||
| 29 | } | ||
| 30 | } | ||
diff --git a/pintos-progos/tests/vm/page-shuffle.ck b/pintos-progos/tests/vm/page-shuffle.ck new file mode 100644 index 0000000..6447d38 --- /dev/null +++ b/pintos-progos/tests/vm/page-shuffle.ck | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::cksum; | ||
| 6 | use tests::lib; | ||
| 7 | |||
| 8 | my ($init, @shuffle); | ||
| 9 | if (1) { | ||
| 10 | # Use precalculated values. | ||
| 11 | $init = 3115322833; | ||
| 12 | @shuffle = (1691062564, 1973575879, 1647619479, 96566261, 3885786467, | ||
| 13 | 3022003332, 3614934266, 2704001777, 735775156, 1864109763); | ||
| 14 | } else { | ||
| 15 | # Recalculate values. | ||
| 16 | my ($buf) = ""; | ||
| 17 | for my $i (0...128 * 1024 - 1) { | ||
| 18 | $buf .= chr (($i * 257) & 0xff); | ||
| 19 | } | ||
| 20 | $init = cksum ($buf); | ||
| 21 | |||
| 22 | random_init (0); | ||
| 23 | for my $i (1...10) { | ||
| 24 | $buf = shuffle ($buf, length ($buf), 1); | ||
| 25 | push (@shuffle, cksum ($buf)); | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | check_expected (IGNORE_EXIT_CODES => 1, [<<EOF]); | ||
| 30 | (page-shuffle) begin | ||
| 31 | (page-shuffle) init: cksum=$init | ||
| 32 | (page-shuffle) shuffle 0: cksum=$shuffle[0] | ||
| 33 | (page-shuffle) shuffle 1: cksum=$shuffle[1] | ||
| 34 | (page-shuffle) shuffle 2: cksum=$shuffle[2] | ||
| 35 | (page-shuffle) shuffle 3: cksum=$shuffle[3] | ||
| 36 | (page-shuffle) shuffle 4: cksum=$shuffle[4] | ||
| 37 | (page-shuffle) shuffle 5: cksum=$shuffle[5] | ||
| 38 | (page-shuffle) shuffle 6: cksum=$shuffle[6] | ||
| 39 | (page-shuffle) shuffle 7: cksum=$shuffle[7] | ||
| 40 | (page-shuffle) shuffle 8: cksum=$shuffle[8] | ||
| 41 | (page-shuffle) shuffle 9: cksum=$shuffle[9] | ||
| 42 | (page-shuffle) end | ||
| 43 | EOF | ||
| 44 | pass; | ||
diff --git a/pintos-progos/tests/vm/parallel-merge.c b/pintos-progos/tests/vm/parallel-merge.c new file mode 100644 index 0000000..cc09bb1 --- /dev/null +++ b/pintos-progos/tests/vm/parallel-merge.c | |||
| @@ -0,0 +1,149 @@ | |||
| 1 | /* Generates about 1 MB of random data that is then divided into | ||
| 2 | 16 chunks. A separate subprocess sorts each chunk; the | ||
| 3 | subprocesses run in parallel. Then we merge the chunks and | ||
| 4 | verify that the result is what it should be. */ | ||
| 5 | |||
| 6 | #include "tests/vm/parallel-merge.h" | ||
| 7 | #include <stdio.h> | ||
| 8 | #include <syscall.h> | ||
| 9 | #include "tests/arc4.h" | ||
| 10 | #include "tests/lib.h" | ||
| 11 | #include "tests/main.h" | ||
| 12 | |||
| 13 | #define CHUNK_SIZE (128 * 1024) | ||
| 14 | #define CHUNK_CNT 8 /* Number of chunks. */ | ||
| 15 | #define DATA_SIZE (CHUNK_CNT * CHUNK_SIZE) /* Buffer size. */ | ||
| 16 | |||
| 17 | unsigned char buf1[DATA_SIZE], buf2[DATA_SIZE]; | ||
| 18 | size_t histogram[256]; | ||
| 19 | |||
| 20 | /* Initialize buf1 with random data, | ||
| 21 | then count the number of instances of each value within it. */ | ||
| 22 | static void | ||
| 23 | init (void) | ||
| 24 | { | ||
| 25 | struct arc4 arc4; | ||
| 26 | size_t i; | ||
| 27 | |||
| 28 | msg ("init"); | ||
| 29 | |||
| 30 | arc4_init (&arc4, "foobar", 6); | ||
| 31 | arc4_crypt (&arc4, buf1, sizeof buf1); | ||
| 32 | for (i = 0; i < sizeof buf1; i++) | ||
| 33 | histogram[buf1[i]]++; | ||
| 34 | } | ||
| 35 | |||
| 36 | /* Sort each chunk of buf1 using SUBPROCESS, | ||
| 37 | which is expected to return EXIT_STATUS. */ | ||
| 38 | static void | ||
| 39 | sort_chunks (const char *subprocess, int exit_status) | ||
| 40 | { | ||
| 41 | pid_t children[CHUNK_CNT]; | ||
| 42 | size_t i; | ||
| 43 | |||
| 44 | for (i = 0; i < CHUNK_CNT; i++) | ||
| 45 | { | ||
| 46 | char fn[128]; | ||
| 47 | char cmd[128]; | ||
| 48 | int handle; | ||
| 49 | |||
| 50 | msg ("sort chunk %zu", i); | ||
| 51 | |||
| 52 | /* Write this chunk to a file. */ | ||
| 53 | snprintf (fn, sizeof fn, "buf%zu", i); | ||
| 54 | create (fn, CHUNK_SIZE); | ||
| 55 | quiet = true; | ||
| 56 | CHECK ((handle = open (fn)) > 1, "open \"%s\"", fn); | ||
| 57 | write (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE); | ||
| 58 | close (handle); | ||
| 59 | |||
| 60 | /* Sort with subprocess. */ | ||
| 61 | snprintf (cmd, sizeof cmd, "%s %s", subprocess, fn); | ||
| 62 | CHECK ((children[i] = exec (cmd)) != -1, "exec \"%s\"", cmd); | ||
| 63 | quiet = false; | ||
| 64 | } | ||
| 65 | |||
| 66 | for (i = 0; i < CHUNK_CNT; i++) | ||
| 67 | { | ||
| 68 | char fn[128]; | ||
| 69 | int handle; | ||
| 70 | |||
| 71 | CHECK (wait (children[i]) == exit_status, "wait for child %zu", i); | ||
| 72 | |||
| 73 | /* Read chunk back from file. */ | ||
| 74 | quiet = true; | ||
| 75 | snprintf (fn, sizeof fn, "buf%zu", i); | ||
| 76 | CHECK ((handle = open (fn)) > 1, "open \"%s\"", fn); | ||
| 77 | read (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE); | ||
| 78 | close (handle); | ||
| 79 | quiet = false; | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | /* Merge the sorted chunks in buf1 into a fully sorted buf2. */ | ||
| 84 | static void | ||
| 85 | merge (void) | ||
| 86 | { | ||
| 87 | unsigned char *mp[CHUNK_CNT]; | ||
| 88 | size_t mp_left; | ||
| 89 | unsigned char *op; | ||
| 90 | size_t i; | ||
| 91 | |||
| 92 | msg ("merge"); | ||
| 93 | |||
| 94 | /* Initialize merge pointers. */ | ||
| 95 | mp_left = CHUNK_CNT; | ||
| 96 | for (i = 0; i < CHUNK_CNT; i++) | ||
| 97 | mp[i] = buf1 + CHUNK_SIZE * i; | ||
| 98 | |||
| 99 | /* Merge. */ | ||
| 100 | op = buf2; | ||
| 101 | while (mp_left > 0) | ||
| 102 | { | ||
| 103 | /* Find smallest value. */ | ||
| 104 | size_t min = 0; | ||
| 105 | for (i = 1; i < mp_left; i++) | ||
| 106 | if (*mp[i] < *mp[min]) | ||
| 107 | min = i; | ||
| 108 | |||
| 109 | /* Append value to buf2. */ | ||
| 110 | *op++ = *mp[min]; | ||
| 111 | |||
| 112 | /* Advance merge pointer. | ||
| 113 | Delete this chunk from the set if it's emptied. */ | ||
| 114 | if ((++mp[min] - buf1) % CHUNK_SIZE == 0) | ||
| 115 | mp[min] = mp[--mp_left]; | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | static void | ||
| 120 | verify (void) | ||
| 121 | { | ||
| 122 | size_t buf_idx; | ||
| 123 | size_t hist_idx; | ||
| 124 | |||
| 125 | msg ("verify"); | ||
| 126 | |||
| 127 | buf_idx = 0; | ||
| 128 | for (hist_idx = 0; hist_idx < sizeof histogram / sizeof *histogram; | ||
| 129 | hist_idx++) | ||
| 130 | { | ||
| 131 | while (histogram[hist_idx]-- > 0) | ||
| 132 | { | ||
| 133 | if (buf2[buf_idx] != hist_idx) | ||
| 134 | fail ("bad value %d in byte %zu", buf2[buf_idx], buf_idx); | ||
| 135 | buf_idx++; | ||
| 136 | } | ||
| 137 | } | ||
| 138 | |||
| 139 | msg ("success, buf_idx=%'zu", buf_idx); | ||
| 140 | } | ||
| 141 | |||
| 142 | void | ||
| 143 | parallel_merge (const char *child_name, int exit_status) | ||
| 144 | { | ||
| 145 | init (); | ||
| 146 | sort_chunks (child_name, exit_status); | ||
| 147 | merge (); | ||
| 148 | verify (); | ||
| 149 | } | ||
diff --git a/pintos-progos/tests/vm/parallel-merge.h b/pintos-progos/tests/vm/parallel-merge.h new file mode 100644 index 0000000..a6b6431 --- /dev/null +++ b/pintos-progos/tests/vm/parallel-merge.h | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | #ifndef TESTS_VM_PARALLEL_MERGE | ||
| 2 | #define TESTS_VM_PARALLEL_MERGE 1 | ||
| 3 | |||
| 4 | void parallel_merge (const char *child_name, int exit_status); | ||
| 5 | |||
| 6 | #endif /* tests/vm/parallel-merge.h */ | ||
diff --git a/pintos-progos/tests/vm/process_death.pm b/pintos-progos/tests/vm/process_death.pm new file mode 100644 index 0000000..52039a1 --- /dev/null +++ b/pintos-progos/tests/vm/process_death.pm | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | |||
| 6 | sub check_process_death { | ||
| 7 | my ($proc_name) = @_; | ||
| 8 | our ($test); | ||
| 9 | my (@output) = read_text_file ("$test.output"); | ||
| 10 | |||
| 11 | common_checks ("run", @output); | ||
| 12 | @output = get_core_output ("run", @output); | ||
| 13 | fail "First line of output is not `($proc_name) begin' message.\n" | ||
| 14 | if $output[0] ne "($proc_name) begin"; | ||
| 15 | fail "Output missing '$proc_name: exit(-1)' message.\n" | ||
| 16 | if !grep ("$proc_name: exit(-1)" eq $_, @output); | ||
| 17 | fail "Output contains '($proc_name) end' message.\n" | ||
| 18 | if grep (/\($proc_name\) end/, @output); | ||
| 19 | pass; | ||
| 20 | } | ||
| 21 | |||
| 22 | 1; | ||
diff --git a/pintos-progos/tests/vm/pt-bad-addr.c b/pintos-progos/tests/vm/pt-bad-addr.c new file mode 100644 index 0000000..3ca4084 --- /dev/null +++ b/pintos-progos/tests/vm/pt-bad-addr.c | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | /* Accesses a bad address. | ||
| 2 | The process must be terminated with -1 exit code. */ | ||
| 3 | |||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | fail ("bad addr read as %d", *(int *) 0x04000000); | ||
| 11 | } | ||
diff --git a/pintos-progos/tests/vm/pt-bad-addr.ck b/pintos-progos/tests/vm/pt-bad-addr.ck new file mode 100644 index 0000000..09ea039 --- /dev/null +++ b/pintos-progos/tests/vm/pt-bad-addr.ck | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::vm::process_death; | ||
| 6 | |||
| 7 | check_process_death ('pt-bad-addr'); | ||
diff --git a/pintos-progos/tests/vm/pt-bad-read.c b/pintos-progos/tests/vm/pt-bad-read.c new file mode 100644 index 0000000..ee791ff --- /dev/null +++ b/pintos-progos/tests/vm/pt-bad-read.c | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | /* Reads from a file into a bad address. | ||
| 2 | The process must be terminated with -1 exit code. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | int handle; | ||
| 12 | |||
| 13 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 14 | read (handle, (char *) &handle - 4096, 1); | ||
| 15 | fail ("survived reading data into bad address"); | ||
| 16 | } | ||
diff --git a/pintos-progos/tests/vm/pt-bad-read.ck b/pintos-progos/tests/vm/pt-bad-read.ck new file mode 100644 index 0000000..1f96bb4 --- /dev/null +++ b/pintos-progos/tests/vm/pt-bad-read.ck | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (pt-bad-read) begin | ||
| 7 | (pt-bad-read) open "sample.txt" | ||
| 8 | pt-bad-read: exit(-1) | ||
| 9 | EOF | ||
| 10 | pass; | ||
diff --git a/pintos-progos/tests/vm/pt-big-stk-obj.c b/pintos-progos/tests/vm/pt-big-stk-obj.c new file mode 100644 index 0000000..6b630ec --- /dev/null +++ b/pintos-progos/tests/vm/pt-big-stk-obj.c | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | /* Allocates and writes to a 64 kB object on the stack. | ||
| 2 | This must succeed. */ | ||
| 3 | |||
| 4 | #include <string.h> | ||
| 5 | #include "tests/arc4.h" | ||
| 6 | #include "tests/cksum.h" | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | void | ||
| 11 | test_main (void) | ||
| 12 | { | ||
| 13 | char stk_obj[65536]; | ||
| 14 | struct arc4 arc4; | ||
| 15 | |||
| 16 | arc4_init (&arc4, "foobar", 6); | ||
| 17 | memset (stk_obj, 0, sizeof stk_obj); | ||
| 18 | arc4_crypt (&arc4, stk_obj, sizeof stk_obj); | ||
| 19 | msg ("cksum: %lu", cksum (stk_obj, sizeof stk_obj)); | ||
| 20 | } | ||
diff --git a/pintos-progos/tests/vm/pt-big-stk-obj.ck b/pintos-progos/tests/vm/pt-big-stk-obj.ck new file mode 100644 index 0000000..eb5853a --- /dev/null +++ b/pintos-progos/tests/vm/pt-big-stk-obj.ck | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (pt-big-stk-obj) begin | ||
| 7 | (pt-big-stk-obj) cksum: 3256410166 | ||
| 8 | (pt-big-stk-obj) end | ||
| 9 | EOF | ||
| 10 | pass; | ||
diff --git a/pintos-progos/tests/vm/pt-grow-bad.c b/pintos-progos/tests/vm/pt-grow-bad.c new file mode 100644 index 0000000..d4beba2 --- /dev/null +++ b/pintos-progos/tests/vm/pt-grow-bad.c | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | /* Read from an address 4,096 bytes below the stack pointer. | ||
| 2 | The process must be terminated with -1 exit code. */ | ||
| 3 | |||
| 4 | #include <string.h> | ||
| 5 | #include "tests/arc4.h" | ||
| 6 | #include "tests/cksum.h" | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | void | ||
| 11 | test_main (void) | ||
| 12 | { | ||
| 13 | asm volatile ("movl -4096(%esp), %eax"); | ||
| 14 | } | ||
diff --git a/pintos-progos/tests/vm/pt-grow-bad.ck b/pintos-progos/tests/vm/pt-grow-bad.ck new file mode 100644 index 0000000..4c0ab8a --- /dev/null +++ b/pintos-progos/tests/vm/pt-grow-bad.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']); | ||
| 6 | (pt-grow-bad) begin | ||
| 7 | pt-grow-bad: exit(-1) | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/vm/pt-grow-pusha.c b/pintos-progos/tests/vm/pt-grow-pusha.c new file mode 100644 index 0000000..f9762a5 --- /dev/null +++ b/pintos-progos/tests/vm/pt-grow-pusha.c | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | /* Expand the stack by 32 bytes all at once using the PUSHA | ||
| 2 | instruction. | ||
| 3 | This must succeed. */ | ||
| 4 | |||
| 5 | #include <string.h> | ||
| 6 | #include "tests/arc4.h" | ||
| 7 | #include "tests/cksum.h" | ||
| 8 | #include "tests/lib.h" | ||
| 9 | #include "tests/main.h" | ||
| 10 | |||
| 11 | void | ||
| 12 | test_main (void) | ||
| 13 | { | ||
| 14 | asm volatile | ||
| 15 | ("movl %%esp, %%eax;" /* Save a copy of the stack pointer. */ | ||
| 16 | "andl $0xfffff000, %%esp;" /* Move stack pointer to bottom of page. */ | ||
| 17 | "pushal;" /* Push 32 bytes on stack at once. */ | ||
| 18 | "movl %%eax, %%esp" /* Restore copied stack pointer. */ | ||
| 19 | : : : "eax"); /* Tell GCC we destroyed eax. */ | ||
| 20 | } | ||
diff --git a/pintos-progos/tests/vm/pt-grow-pusha.ck b/pintos-progos/tests/vm/pt-grow-pusha.ck new file mode 100644 index 0000000..5000966 --- /dev/null +++ b/pintos-progos/tests/vm/pt-grow-pusha.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (pt-grow-pusha) begin | ||
| 7 | (pt-grow-pusha) end | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/vm/pt-grow-stack.c b/pintos-progos/tests/vm/pt-grow-stack.c new file mode 100644 index 0000000..0997a00 --- /dev/null +++ b/pintos-progos/tests/vm/pt-grow-stack.c | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | /* Demonstrate that the stack can grow. | ||
| 2 | This must succeed. */ | ||
| 3 | |||
| 4 | #include <string.h> | ||
| 5 | #include "tests/arc4.h" | ||
| 6 | #include "tests/cksum.h" | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | void | ||
| 11 | test_main (void) | ||
| 12 | { | ||
| 13 | char stack_obj[4096]; | ||
| 14 | struct arc4 arc4; | ||
| 15 | |||
| 16 | arc4_init (&arc4, "foobar", 6); | ||
| 17 | memset (stack_obj, 0, sizeof stack_obj); | ||
| 18 | arc4_crypt (&arc4, stack_obj, sizeof stack_obj); | ||
| 19 | msg ("cksum: %lu", cksum (stack_obj, sizeof stack_obj)); | ||
| 20 | } | ||
diff --git a/pintos-progos/tests/vm/pt-grow-stack.ck b/pintos-progos/tests/vm/pt-grow-stack.ck new file mode 100644 index 0000000..1e669db --- /dev/null +++ b/pintos-progos/tests/vm/pt-grow-stack.ck | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (pt-grow-stack) begin | ||
| 7 | (pt-grow-stack) cksum: 3424492700 | ||
| 8 | (pt-grow-stack) end | ||
| 9 | EOF | ||
| 10 | pass; | ||
diff --git a/pintos-progos/tests/vm/pt-grow-stk-sc.c b/pintos-progos/tests/vm/pt-grow-stk-sc.c new file mode 100644 index 0000000..3efbb5f --- /dev/null +++ b/pintos-progos/tests/vm/pt-grow-stk-sc.c | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | /* This test checks that the stack is properly extended even if | ||
| 2 | the first access to a stack location occurs inside a system | ||
| 3 | call. | ||
| 4 | |||
| 5 | From Godmar Back. */ | ||
| 6 | |||
| 7 | #include <string.h> | ||
| 8 | #include <syscall.h> | ||
| 9 | #include "tests/vm/sample.inc" | ||
| 10 | #include "tests/lib.h" | ||
| 11 | #include "tests/main.h" | ||
| 12 | |||
| 13 | void | ||
| 14 | test_main (void) | ||
| 15 | { | ||
| 16 | int handle; | ||
| 17 | int slen = strlen (sample); | ||
| 18 | char buf2[65536]; | ||
| 19 | |||
| 20 | /* Write file via write(). */ | ||
| 21 | CHECK (create ("sample.txt", slen), "create \"sample.txt\""); | ||
| 22 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 23 | CHECK (write (handle, sample, slen) == slen, "write \"sample.txt\""); | ||
| 24 | close (handle); | ||
| 25 | |||
| 26 | /* Read back via read(). */ | ||
| 27 | CHECK ((handle = open ("sample.txt")) > 1, "2nd open \"sample.txt\""); | ||
| 28 | CHECK (read (handle, buf2 + 32768, slen) == slen, "read \"sample.txt\""); | ||
| 29 | |||
| 30 | CHECK (!memcmp (sample, buf2 + 32768, slen), "compare written data against read data"); | ||
| 31 | close (handle); | ||
| 32 | } | ||
diff --git a/pintos-progos/tests/vm/pt-grow-stk-sc.ck b/pintos-progos/tests/vm/pt-grow-stk-sc.ck new file mode 100644 index 0000000..23d3b02 --- /dev/null +++ b/pintos-progos/tests/vm/pt-grow-stk-sc.ck | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (pt-grow-stk-sc) begin | ||
| 7 | (pt-grow-stk-sc) create "sample.txt" | ||
| 8 | (pt-grow-stk-sc) open "sample.txt" | ||
| 9 | (pt-grow-stk-sc) write "sample.txt" | ||
| 10 | (pt-grow-stk-sc) 2nd open "sample.txt" | ||
| 11 | (pt-grow-stk-sc) read "sample.txt" | ||
| 12 | (pt-grow-stk-sc) compare written data against read data | ||
| 13 | (pt-grow-stk-sc) end | ||
| 14 | EOF | ||
| 15 | pass; | ||
diff --git a/pintos-progos/tests/vm/pt-write-code-2.c b/pintos-progos/tests/vm/pt-write-code-2.c new file mode 100644 index 0000000..83bcc2c --- /dev/null +++ b/pintos-progos/tests/vm/pt-write-code-2.c | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | /* Try to write to the code segment using a system call. | ||
| 2 | The process must be terminated with -1 exit code. */ | ||
| 3 | |||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | int handle; | ||
| 11 | |||
| 12 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 13 | read (handle, (void *) test_main, 1); | ||
| 14 | fail ("survived reading data into code segment"); | ||
| 15 | } | ||
diff --git a/pintos-progos/tests/vm/pt-write-code.c b/pintos-progos/tests/vm/pt-write-code.c new file mode 100644 index 0000000..5072cec --- /dev/null +++ b/pintos-progos/tests/vm/pt-write-code.c | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | /* Try to write to the code segment. | ||
| 2 | The process must be terminated with -1 exit code. */ | ||
| 3 | |||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | *(int *) test_main = 0; | ||
| 11 | fail ("writing the code segment succeeded"); | ||
| 12 | } | ||
diff --git a/pintos-progos/tests/vm/pt-write-code.ck b/pintos-progos/tests/vm/pt-write-code.ck new file mode 100644 index 0000000..65610fb --- /dev/null +++ b/pintos-progos/tests/vm/pt-write-code.ck | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::vm::process_death; | ||
| 6 | |||
| 7 | check_process_death ('pt-write-code'); | ||
diff --git a/pintos-progos/tests/vm/pt-write-code2.ck b/pintos-progos/tests/vm/pt-write-code2.ck new file mode 100644 index 0000000..69ffc77 --- /dev/null +++ b/pintos-progos/tests/vm/pt-write-code2.ck | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (pt-write-code2) begin | ||
| 7 | (pt-write-code2) open "sample.txt" | ||
| 8 | pt-write-code2: exit(-1) | ||
| 9 | EOF | ||
| 10 | pass; | ||
diff --git a/pintos-progos/tests/vm/qsort.c b/pintos-progos/tests/vm/qsort.c new file mode 100644 index 0000000..922572c --- /dev/null +++ b/pintos-progos/tests/vm/qsort.c | |||
| @@ -0,0 +1,136 @@ | |||
| 1 | #include "tests/vm/qsort.h" | ||
| 2 | #include <stdbool.h> | ||
| 3 | #include <debug.h> | ||
| 4 | #include <random.h> | ||
| 5 | |||
| 6 | /* Picks a pivot for the quicksort from the SIZE bytes in BUF. */ | ||
| 7 | static unsigned char | ||
| 8 | pick_pivot (unsigned char *buf, size_t size) | ||
| 9 | { | ||
| 10 | ASSERT (size >= 1); | ||
| 11 | return buf[random_ulong () % size]; | ||
| 12 | } | ||
| 13 | |||
| 14 | /* Checks whether the SIZE bytes in ARRAY are divided into an | ||
| 15 | initial LEFT_SIZE elements all less than PIVOT followed by | ||
| 16 | SIZE - LEFT_SIZE elements all greater than or equal to | ||
| 17 | PIVOT. */ | ||
| 18 | static bool | ||
| 19 | is_partitioned (const unsigned char *array, size_t size, | ||
| 20 | unsigned char pivot, size_t left_size) | ||
| 21 | { | ||
| 22 | size_t i; | ||
| 23 | |||
| 24 | for (i = 0; i < left_size; i++) | ||
| 25 | if (array[i] >= pivot) | ||
| 26 | return false; | ||
| 27 | |||
| 28 | for (; i < size; i++) | ||
| 29 | if (array[i] < pivot) | ||
| 30 | return false; | ||
| 31 | |||
| 32 | return true; | ||
| 33 | } | ||
| 34 | |||
| 35 | /* Swaps the bytes at *A and *B. */ | ||
| 36 | static void | ||
| 37 | swap (unsigned char *a, unsigned char *b) | ||
| 38 | { | ||
| 39 | unsigned char t = *a; | ||
| 40 | *a = *b; | ||
| 41 | *b = t; | ||
| 42 | } | ||
| 43 | |||
| 44 | /* Partitions ARRAY in-place in an initial run of bytes all less | ||
| 45 | than PIVOT, followed by a run of bytes all greater than or | ||
| 46 | equal to PIVOT. Returns the length of the initial run. */ | ||
| 47 | static size_t | ||
| 48 | partition (unsigned char *array, size_t size, int pivot) | ||
| 49 | { | ||
| 50 | size_t left_size = size; | ||
| 51 | unsigned char *first = array; | ||
| 52 | unsigned char *last = first + left_size; | ||
| 53 | |||
| 54 | for (;;) | ||
| 55 | { | ||
| 56 | /* Move FIRST forward to point to first element greater than | ||
| 57 | PIVOT. */ | ||
| 58 | for (;;) | ||
| 59 | { | ||
| 60 | if (first == last) | ||
| 61 | { | ||
| 62 | ASSERT (is_partitioned (array, size, pivot, left_size)); | ||
| 63 | return left_size; | ||
| 64 | } | ||
| 65 | else if (*first >= pivot) | ||
| 66 | break; | ||
| 67 | |||
| 68 | first++; | ||
| 69 | } | ||
| 70 | left_size--; | ||
| 71 | |||
| 72 | /* Move LAST backward to point to last element no bigger | ||
| 73 | than PIVOT. */ | ||
| 74 | for (;;) | ||
| 75 | { | ||
| 76 | last--; | ||
| 77 | |||
| 78 | if (first == last) | ||
| 79 | { | ||
| 80 | ASSERT (is_partitioned (array, size, pivot, left_size)); | ||
| 81 | return left_size; | ||
| 82 | } | ||
| 83 | else if (*last < pivot) | ||
| 84 | break; | ||
| 85 | else | ||
| 86 | left_size--; | ||
| 87 | } | ||
| 88 | |||
| 89 | /* By swapping FIRST and LAST we extend the starting and | ||
| 90 | ending sequences that pass and fail, respectively, | ||
| 91 | PREDICATE. */ | ||
| 92 | swap (first, last); | ||
| 93 | first++; | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | /* Returns true if the SIZE bytes in BUF are in nondecreasing | ||
| 98 | order, false otherwise. */ | ||
| 99 | static bool | ||
| 100 | is_sorted (const unsigned char *buf, size_t size) | ||
| 101 | { | ||
| 102 | size_t i; | ||
| 103 | |||
| 104 | for (i = 1; i < size; i++) | ||
| 105 | if (buf[i - 1] > buf[i]) | ||
| 106 | return false; | ||
| 107 | |||
| 108 | return true; | ||
| 109 | } | ||
| 110 | |||
| 111 | /* Sorts the SIZE bytes in BUF into nondecreasing order, using | ||
| 112 | the quick-sort algorithm. */ | ||
| 113 | void | ||
| 114 | qsort_bytes (unsigned char *buf, size_t size) | ||
| 115 | { | ||
| 116 | if (!is_sorted (buf, size)) | ||
| 117 | { | ||
| 118 | int pivot = pick_pivot (buf, size); | ||
| 119 | |||
| 120 | unsigned char *left_half = buf; | ||
| 121 | size_t left_size = partition (buf, size, pivot); | ||
| 122 | unsigned char *right_half = left_half + left_size; | ||
| 123 | size_t right_size = size - left_size; | ||
| 124 | |||
| 125 | if (left_size <= right_size) | ||
| 126 | { | ||
| 127 | qsort_bytes (left_half, left_size); | ||
| 128 | qsort_bytes (right_half, right_size); | ||
| 129 | } | ||
| 130 | else | ||
| 131 | { | ||
| 132 | qsort_bytes (right_half, right_size); | ||
| 133 | qsort_bytes (left_half, left_size); | ||
| 134 | } | ||
| 135 | } | ||
| 136 | } | ||
diff --git a/pintos-progos/tests/vm/qsort.h b/pintos-progos/tests/vm/qsort.h new file mode 100644 index 0000000..61b65f3 --- /dev/null +++ b/pintos-progos/tests/vm/qsort.h | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | #ifndef TESTS_VM_QSORT_H | ||
| 2 | #define TESTS_VM_QSORT_H 1 | ||
| 3 | |||
| 4 | #include <stddef.h> | ||
| 5 | |||
| 6 | void qsort_bytes (unsigned char *buf, size_t size); | ||
| 7 | |||
| 8 | #endif /* tests/vm/qsort.h */ | ||
diff --git a/pintos-progos/tests/vm/sample.inc b/pintos-progos/tests/vm/sample.inc new file mode 100644 index 0000000..a60a139 --- /dev/null +++ b/pintos-progos/tests/vm/sample.inc | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | char sample[] = { | ||
| 2 | "=== ALL USERS PLEASE NOTE ========================\n" | ||
| 3 | "\n" | ||
| 4 | "CAR and CDR now return extra values.\n" | ||
| 5 | "\n" | ||
| 6 | "The function CAR now returns two values. Since it has to go to the\n" | ||
| 7 | "trouble to figure out if the object is carcdr-able anyway, we figured\n" | ||
| 8 | "you might as well get both halves at once. For example, the following\n" | ||
| 9 | "code shows how to destructure a cons (SOME-CONS) into its two slots\n" | ||
| 10 | "(THE-CAR and THE-CDR):\n" | ||
| 11 | "\n" | ||
| 12 | " (MULTIPLE-VALUE-BIND (THE-CAR THE-CDR) (CAR SOME-CONS) ...)\n" | ||
| 13 | "\n" | ||
| 14 | "For symmetry with CAR, CDR returns a second value which is the CAR of\n" | ||
| 15 | "the object. In a related change, the functions MAKE-ARRAY and CONS\n" | ||
| 16 | "have been fixed so they don't allocate any storage except on the\n" | ||
| 17 | "stack. This should hopefully help people who don't like using the\n" | ||
| 18 | "garbage collector because it cold boots the machine so often.\n" | ||
| 19 | }; | ||
diff --git a/pintos-progos/tests/vm/sample.txt b/pintos-progos/tests/vm/sample.txt new file mode 100644 index 0000000..c446830 --- /dev/null +++ b/pintos-progos/tests/vm/sample.txt | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | === ALL USERS PLEASE NOTE ======================== | ||
| 2 | |||
| 3 | CAR and CDR now return extra values. | ||
| 4 | |||
| 5 | The function CAR now returns two values. Since it has to go to the | ||
| 6 | trouble to figure out if the object is carcdr-able anyway, we figured | ||
| 7 | you might as well get both halves at once. For example, the following | ||
| 8 | code shows how to destructure a cons (SOME-CONS) into its two slots | ||
| 9 | (THE-CAR and THE-CDR): | ||
| 10 | |||
| 11 | (MULTIPLE-VALUE-BIND (THE-CAR THE-CDR) (CAR SOME-CONS) ...) | ||
| 12 | |||
| 13 | For symmetry with CAR, CDR returns a second value which is the CAR of | ||
| 14 | the object. In a related change, the functions MAKE-ARRAY and CONS | ||
| 15 | have been fixed so they don't allocate any storage except on the | ||
| 16 | stack. This should hopefully help people who don't like using the | ||
| 17 | garbage collector because it cold boots the machine so often. | ||
