1use Mojo::Base -strict; 2 3use Test::More; 4use Carp qw(croak); 5use Config; 6use Mojo::Asset::File; 7use Mojo::Asset::Memory; 8use Mojo::File qw(path tempdir); 9 10subtest 'File asset' => sub { 11 my $file = Mojo::Asset::File->new; 12 is $file->size, 0, 'file is empty'; 13 is $file->mtime, (stat $file->handle)[9], 'right mtime'; 14 is $file->slurp, '', 'file is empty'; 15 $file->add_chunk('abc'); 16 is $file->contains('abc'), 0, '"abc" at position 0'; 17 is $file->contains('bc'), 1, '"bc" at position 1'; 18 is $file->contains('db'), -1, 'does not contain "db"'; 19 is $file->size, 3, 'right size'; 20 is $file->mtime, (stat $file->handle)[9], 'right mtime'; 21 is $file->to_file, $file, 'same object'; 22 23 my $path = $file->path; 24 ok -e $path, 'temporary file exists'; 25 undef $file; 26 ok !-e $path, 'temporary file has been cleaned up'; 27}; 28 29subtest 'Memory asset' => sub { 30 my $mem = Mojo::Asset::Memory->new; 31 $mem->add_chunk('abc'); 32 is $mem->contains('abc'), 0, '"abc" at position 0'; 33 is $mem->contains('bc'), 1, '"bc" at position 1'; 34 is $mem->contains('db'), -1, 'does not contain "db"'; 35 is $mem->size, 3, 'right size'; 36 is $mem->mtime, $^T, 'right mtime'; 37 is $mem->mtime, Mojo::Asset::Memory->new->mtime, 'same mtime'; 38 my $mtime = $mem->mtime; 39 is $mem->mtime($mtime + 23)->mtime, $mtime + 23, 'right mtime'; 40}; 41 42subtest 'Asset upgrade from memory to file' => sub { 43 my $mem = Mojo::Asset::Memory->new; 44 $mem->add_chunk('abcdef'); 45 isa_ok $mem->to_file, 'Mojo::Asset::File', 'right class'; 46 is $mem->to_file->slurp, $mem->slurp, 'same content'; 47 my $file = $mem->to_file; 48 my $path = $file->path; 49 ok -e $path, 'file exists'; 50 undef $file; 51 ok !-e $path, 'file has been cleaned up'; 52}; 53 54subtest 'Empty file asset' => sub { 55 my $file = Mojo::Asset::File->new; 56 is $file->size, 0, 'asset is empty'; 57 is $file->get_chunk(0), '', 'no content'; 58 is $file->slurp, '', 'no content'; 59 is $file->contains('a'), -1, 'does not contain "a"'; 60}; 61 62subtest 'Empty memory asset' => sub { 63 my $mem = Mojo::Asset::Memory->new; 64 is $mem->size, 0, 'asset is empty'; 65 is $mem->get_chunk(0), '', 'no content'; 66 is $mem->slurp, '', 'no content'; 67 ok !$mem->is_range, 'no range'; 68 is $mem->contains('a'), -1, 'does not contain "a"'; 69}; 70 71subtest 'File asset range support (a[bcdefabc])' => sub { 72 my $file = Mojo::Asset::File->new(start_range => 1); 73 ok $file->is_range, 'has range'; 74 $file->add_chunk('abcdefabc'); 75 is $file->contains('bcdef'), 0, '"bcdef" at position 0'; 76 is $file->contains('cdef'), 1, '"cdef" at position 1'; 77 is $file->contains('abc'), 5, '"abc" at position 5'; 78 is $file->contains('db'), -1, 'does not contain "db"'; 79}; 80 81subtest 'Memory asset range support (a[bcdefabc])' => sub { 82 my $mem = Mojo::Asset::Memory->new(start_range => 1); 83 ok $mem->is_range, 'has range'; 84 $mem->add_chunk('abcdefabc'); 85 is $mem->contains('bcdef'), 0, '"bcdef" at position 0'; 86 is $mem->contains('cdef'), 1, '"cdef" at position 1'; 87 is $mem->contains('abc'), 5, '"abc" at position 5'; 88 is $mem->contains('db'), -1, 'does not contain "db"'; 89}; 90 91subtest 'File asset range support (ab[cdefghi]jk)' => sub { 92 my $file = Mojo::Asset::File->new(start_range => 2, end_range => 8); 93 ok $file->is_range, 'has range'; 94 $file->add_chunk('abcdefghijk'); 95 is $file->contains('cdefghi'), 0, '"cdefghi" at position 0'; 96 is $file->contains('fghi'), 3, '"fghi" at position 3'; 97 is $file->contains('f'), 3, '"f" at position 3'; 98 is $file->contains('hi'), 5, '"hi" at position 5'; 99 is $file->contains('db'), -1, 'does not contain "db"'; 100 is $file->get_chunk(0), 'cdefghi', 'chunk from position 0'; 101 is $file->get_chunk(1), 'defghi', 'chunk from position 1'; 102 is $file->get_chunk(5), 'hi', 'chunk from position 5'; 103 is $file->get_chunk(0, 2), 'cd', 'chunk from position 0 (2 bytes)'; 104 is $file->get_chunk(1, 3), 'def', 'chunk from position 1 (3 bytes)'; 105 is $file->get_chunk(5, 1), 'h', 'chunk from position 5 (1 byte)'; 106 is $file->get_chunk(5, 3), 'hi', 'chunk from position 5 (2 byte)'; 107}; 108 109subtest 'Memory asset range support (ab[cdefghi]jk)' => sub { 110 my $mem = Mojo::Asset::Memory->new(start_range => 2, end_range => 8); 111 ok $mem->is_range, 'has range'; 112 $mem->add_chunk('abcdefghijk'); 113 is $mem->contains('cdefghi'), 0, '"cdefghi" at position 0'; 114 is $mem->contains('fghi'), 3, '"fghi" at position 3'; 115 is $mem->contains('f'), 3, '"f" at position 3'; 116 is $mem->contains('hi'), 5, '"hi" at position 5'; 117 is $mem->contains('ij'), -1, 'does not contain "ij"'; 118 is $mem->contains('db'), -1, 'does not contain "db"'; 119 is $mem->get_chunk(0), 'cdefghi', 'chunk from position 0'; 120 is $mem->get_chunk(1), 'defghi', 'chunk from position 1'; 121 is $mem->get_chunk(5), 'hi', 'chunk from position 5'; 122 is $mem->get_chunk(0, 2), 'cd', 'chunk from position 0 (2 bytes)'; 123 is $mem->get_chunk(1, 3), 'def', 'chunk from position 1 (3 bytes)'; 124 is $mem->get_chunk(5, 1), 'h', 'chunk from position 5 (1 byte)'; 125 is $mem->get_chunk(5, 3), 'hi', 'chunk from position 5 (2 byte)'; 126}; 127 128subtest 'Huge file asset' => sub { 129 my $file = Mojo::Asset::File->new; 130 ok !$file->is_range, 'no range'; 131 $file->add_chunk('a' x 131072); 132 $file->add_chunk('b'); 133 $file->add_chunk('c' x 131072); 134 $file->add_chunk('ddd'); 135 is $file->contains('a'), 0, '"a" at position 0'; 136 is $file->contains('b'), 131072, '"b" at position 131072'; 137 is $file->contains('c'), 131073, '"c" at position 131073'; 138 is $file->contains('abc'), 131071, '"abc" at position 131071'; 139 is $file->contains('ccdd'), 262143, '"ccdd" at position 262143'; 140 is $file->contains('dd'), 262145, '"dd" at position 262145'; 141 is $file->contains('ddd'), 262145, '"ddd" at position 262145'; 142 is $file->contains('e'), -1, 'does not contain "e"'; 143 is $file->contains('a' x 131072), 0, '"a" x 131072 at position 0'; 144 is $file->contains('c' x 131072), 131073, '"c" x 131072 at position 131073'; 145 is $file->contains('b' . ('c' x 131072) . "ddd"), 131072, '"b" . ("c" x 131072) . "ddd" at position 131072'; 146}; 147 148subtest 'Huge file asset with range' => sub { 149 my $file = Mojo::Asset::File->new(start_range => 1, end_range => 262146); 150 $file->add_chunk('a' x 131072); 151 $file->add_chunk('b'); 152 $file->add_chunk('c' x 131072); 153 $file->add_chunk('ddd'); 154 is $file->contains('a'), 0, '"a" at position 0'; 155 is $file->contains('b'), 131071, '"b" at position 131071'; 156 is $file->contains('c'), 131072, '"c" at position 131072'; 157 is $file->contains('abc'), 131070, '"abc" at position 131070'; 158 is $file->contains('ccdd'), 262142, '"ccdd" at position 262142'; 159 is $file->contains('dd'), 262144, '"dd" at position 262144'; 160 is $file->contains('ddd'), -1, 'does not contain "ddd"'; 161 is $file->contains('b' . ('c' x 131072) . 'ddd'), -1, 'does not contain "b" . ("c" x 131072) . "ddd"'; 162}; 163 164subtest 'Move memory asset to file' => sub { 165 my $mem = Mojo::Asset::Memory->new->add_chunk('abc'); 166 my $tmp = Mojo::Asset::File->new->add_chunk('x'); 167 my $path = $tmp->path; 168 ok -e $path, 'file exists'; 169 undef $tmp; 170 ok !-e $path, 'file has been cleaned up'; 171 is $mem->move_to($path)->slurp, 'abc', 'right content'; 172 ok -e $path, 'file exists'; 173 ok unlink($path), 'unlinked file'; 174 ok !-e $path, 'file has been cleaned up'; 175 is(Mojo::Asset::Memory->new->move_to($path)->slurp, '', 'no content'); 176 ok -e $path, 'file exists'; 177 ok unlink($path), 'unlinked file'; 178 ok !-e $path, 'file has been cleaned up'; 179}; 180 181subtest 'Move file asset to file' => sub { 182 my $file = Mojo::Asset::File->new; 183 $file->add_chunk('bcd'); 184 my $tmp = Mojo::Asset::File->new; 185 $tmp->add_chunk('x'); 186 isnt $file->path, $tmp->path, 'different paths'; 187 my $path = $tmp->path; 188 ok -e $path, 'file exists'; 189 undef $tmp; 190 ok !-e $path, 'file has been cleaned up'; 191 is $file->move_to($path)->slurp, 'bcd', 'right content'; 192 undef $file; 193 ok -e $path, 'file exists'; 194 ok unlink($path), 'unlinked file'; 195 ok !-e $path, 'file has been cleaned up'; 196 is(Mojo::Asset::File->new->move_to($path)->slurp, '', 'no content'); 197 ok -e $path, 'file exists'; 198 ok unlink($path), 'unlinked file'; 199 ok !-e $path, 'file has been cleaned up'; 200}; 201 202subtest 'Upgrade' => sub { 203 my $asset = Mojo::Asset::Memory->new(max_memory_size => 5, auto_upgrade => 1); 204 my $upgrade; 205 $asset->on(upgrade => sub { $upgrade++ }); 206 $asset = $asset->add_chunk('lala'); 207 ok !$upgrade, 'upgrade event has not been emitted'; 208 ok !$asset->is_file, 'stored in memory'; 209 $asset = $asset->add_chunk('lala'); 210 is $upgrade, 1, 'upgrade event has been emitted once'; 211 ok $asset->is_file, 'stored in file'; 212 $asset = $asset->add_chunk('lala'); 213 is $upgrade, 1, 'upgrade event was not emitted again'; 214 ok $asset->is_file, 'stored in file'; 215 is $asset->slurp, 'lalalalalala', 'right content'; 216 ok $asset->cleanup, 'file will be cleaned up'; 217 $asset = Mojo::Asset::Memory->new(max_memory_size => 5); 218 $asset = $asset->add_chunk('lala'); 219 ok !$asset->is_file, 'stored in memory'; 220 $asset = $asset->add_chunk('lala'); 221 ok !$asset->is_file, 'stored in memory'; 222}; 223 224subtest 'Change temporary directory during upgrade' => sub { 225 my $tmpdir = tempdir; 226 my $mem = Mojo::Asset::Memory->new(auto_upgrade => 1, max_memory_size => 10); 227 $mem->on( 228 upgrade => sub { 229 my ($mem, $file) = @_; 230 $file->tmpdir($tmpdir); 231 } 232 ); 233 my $file = $mem->add_chunk('aaaaaaaaaaa'); 234 ok $file->is_file, 'stored in file'; 235 is $file->slurp, 'aaaaaaaaaaa', 'right content'; 236 is path($file->path)->dirname, $tmpdir, 'right directory'; 237}; 238 239subtest 'Temporary directory' => sub { 240 local $ENV{MOJO_TMPDIR} = my $tmpdir = tempdir; 241 my $file = Mojo::Asset::File->new; 242 is($file->tmpdir, $tmpdir, 'same directory'); 243 $file->add_chunk('works!'); 244 is $file->slurp, 'works!', 'right content'; 245 is path($file->path)->dirname, $tmpdir, 'same directory'; 246}; 247 248subtest 'Custom temporary file' => sub { 249 my $tmpdir = tempdir; 250 my $path = $tmpdir->child('test.file'); 251 ok !-e $path, 'file does not exist'; 252 my $file = Mojo::Asset::File->new(path => $path); 253 is $file->path, $path, 'right path'; 254 ok !-e $path, 'file still does not exist'; 255 $file->add_chunk('works!'); 256 ok -e $path, 'file exists'; 257 is $file->slurp, 'works!', 'right content'; 258 undef $file; 259 ok !-e $path, 'file has been cleaned up'; 260}; 261 262subtest 'Temporary file without cleanup' => sub { 263 my $file = Mojo::Asset::File->new(cleanup => 0)->add_chunk('test'); 264 ok $file->is_file, 'stored in file'; 265 is $file->slurp, 'test', 'right content'; 266 is $file->size, 4, 'right size'; 267 is $file->mtime, (stat $file->handle)[9], 'right mtime'; 268 is $file->contains('es'), 1, '"es" at position 1'; 269 my $path = $file->path; 270 undef $file; 271 ok -e $path, 'file exists'; 272 ok unlink($path), 'unlinked file'; 273 ok !-e $path, 'file has been cleaned up'; 274}; 275 276subtest 'Incomplete write' => sub { 277 no warnings 'redefine'; 278 local *IO::Handle::syswrite = sub { $! = 0; 2 }; 279 eval { Mojo::Asset::File->new->add_chunk('test') }; 280 like $@, qr/Can't write to asset: .*/, 'right error'; 281}; 282 283subtest 'Forked process' => sub { 284 plan skip_all => 'Real fork is required!' if $Config{d_pseudofork}; 285 my $file = Mojo::Asset::File->new->add_chunk('Fork test!'); 286 my $path = $file->path; 287 ok -e $path, 'file exists'; 288 is $file->slurp, 'Fork test!', 'right content'; 289 croak "Can't fork: $!" unless defined(my $pid = fork); 290 exit 0 unless $pid; 291 waitpid $pid, 0 if $pid; 292 ok -e $path, 'file still exists'; 293 is $file->slurp, 'Fork test!', 'right content'; 294 undef $file; 295 ok !-e $path, 'file has been cleaned up'; 296}; 297 298subtest 'Abstract methods' => sub { 299 eval { Mojo::Asset->add_chunk }; 300 like $@, qr/Method "add_chunk" not implemented by subclass/, 'right error'; 301 eval { Mojo::Asset->contains }; 302 like $@, qr/Method "contains" not implemented by subclass/, 'right error'; 303 eval { Mojo::Asset->get_chunk }; 304 like $@, qr/Method "get_chunk" not implemented by subclass/, 'right error'; 305 eval { Mojo::Asset->move_to }; 306 like $@, qr/Method "move_to" not implemented by subclass/, 'right error'; 307 eval { Mojo::Asset->mtime }; 308 like $@, qr/Method "mtime" not implemented by subclass/, 'right error'; 309 eval { Mojo::Asset->size }; 310 like $@, qr/Method "size" not implemented by subclass/, 'right error'; 311 eval { Mojo::Asset->slurp }; 312 like $@, qr/Method "slurp" not implemented by subclass/, 'right error'; 313 eval { Mojo::Asset->to_file }; 314 like $@, qr/Method "to_file" not implemented by subclass/, 'right error'; 315}; 316 317done_testing(); 318