view _test/Test/Web/View.pm @ 286:d357b5d85d25

*TTView refactoring
author sergey
date Mon, 18 Feb 2013 14:46:06 +0400
parents c6d0f889ef87
children 2d253e6e4a88
line wrap: on
line source

package Test::Web::View;
use IMPL::Profiler::Memory;
use strict;
use warnings;
use utf8;

use parent qw(IMPL::Test::Unit);
__PACKAGE__->PassThroughArgs;

use File::Slurp;
use Scalar::Util qw(weaken);

use IMPL::lang;
use IMPL::Test qw(assert assertarray test GetCallerSourceLine);
use IMPL::Web::View::TTLoader();

use constant {
    TTLoader => 'IMPL::Web::View::TTLoader',
    MProfiler => 'IMPL::Profiler::Memory'
};

sub AssertMemoryLeak {
    my $code = shift;
    my $dump = shift;
    
    my $data = MProfiler->Monitor($code);
    
    if ($data->isLeak and $dump) {
        write_file("dump.out", { binmode => ':utf8' }, $data->Dump() );
    }
    
    assert( not($data->isLeak), "Memory leak detected", GetCallerSourceLine()  , @{$data->{objects}} );    
}

sub templatesDir {
    $_[0]->GetResourceDir('Resources','TTView');
}

sub CreateLoader {
    my ($this) = @_;
    
    my $loader = TTLoader->new(
        {
            INCLUDE_PATH => [
                $this->templatesDir
            ],
            INTERPOLATE => 1,
            POST_CHOMP => 1,
            ENCODING => 'utf-8'
        },
        ext => '.tt',
        initializer => 'global.tt',
        globals => {
            site => {
                name => 'Test Site'
            },
            date => {
                now => sub { localtime(time); }
            },
            dynamic => sub { 'this is a dynamic value' },
            view => {
            }
        },
        layoutBase => 'Layout'
    );
}

test TTLoaderTests => sub {
    my ($this) = @_;
    
    my $loader = $this->CreateLoader();
    
    # test the loader to be able to find a desired resource
    assert( defined($loader->template('simple') ) );
    
    # loader should be initialized on demand
    assert( not $loader->isInitialized );
    
    # loader should be able to load a document
    my $doc = $loader->document('simple');
    assert(defined $doc);
    
    assert( $loader->isInitialized );
    assert( $loader->context->stash->get('user') eq 'test_user');
    
    # document should inherit loader's context 
    assert( $doc->context->stash->get('user') eq 'test_user');
    
    # document should not have 'this' template variable
    assert( not $doc->templateVars('this') );
    
    assert( $doc->context != $loader->context); # document should have an own context
};

test TTDocumentTests => sub {
    my ($this) = @_;
    my $loader = $this->CreateLoader();
    
    my $doc = $loader->document('simple');
    
    assert(defined $doc);
    $doc->title('test document');
    
    assert($doc->name eq 'document');
    assert($doc->title eq 'test document');
    
    assert(not $doc->can('notexists')); # autoloaded property should be ignored
    assert(not defined $doc->notexists); # nonexisting property 
    assert($doc->template->version == 10); # static metadata
    assert($doc->templateVars('notexists') eq ''); #nonexisting template variable
    assert($doc->templateVars('user') eq 'test_user'); # global data
    assert($doc->templateVars('templateVar') eq ''); # defined in CTOR block, should be local
    assert($doc->templateVars('dynamic') eq 'this is a dynamic value');
    
    my $text = $doc->Render();
    my $expected = read_file($this->GetResourceFile('Resources','TTView.Output','simple.txt'), binmode => ':utf8');
    
    assert($text eq $expected, "Bad Render() output","Got: $text", "Expected: $expected");
    
};

test TTControlTests => sub {
    my ($this) = @_;
    
    my $loader = $this->CreateLoader();
    
    my $doc = $loader->document('simple');
    
    assert(defined $doc);
    
    my $factory = $doc->RequireControl('My/Org/Panel');
    
    assert(defined $factory);
    
    # control factory shares document scope to perform an initialization on demand
    assert($factory->context->stash == $doc->context->stash);
    
    assert($factory == $doc->RequireControl('My/Org/Panel'), "Control should be loaded only once");
    
    my $ctl = $factory->new('information', { visualClass => 'simple', data => ['one','two','hello world'] } );
    
    assert(defined $ctl);    
    
    assert($ctl->name eq 'information', "Created control should have a name", "Got: ".$ctl->name, "Expected: information");
    
    assert($ctl->GetAttribute('visualClass') eq 'simple');
    
    assert($factory->instances == 1);
    
    $doc->childNodes([$ctl]);
    
    assert($doc->templateVars('dojo.require'));
    assert(ref $doc->templateVars('dojo.require') eq 'ARRAY');
    assert($doc->templateVars('dojo.require')->[0] eq 'dijit.form.Input' );
    
    my $text = $ctl->Render();
    
    my $expected = read_file($this->GetResourceFile('Resources', 'TTView.Output', 'Panel.txt'), binmode => ':utf8');    
    assert($text eq $expected, '$ctl->Render(): Bad output', "Got:      $text", "Expected: $expected");
    
    
    
};

test TestDocumentLayout => sub {
    my ($this) = @_;
    
    my $loader = $this->CreateLoader();
    
    my $doc = $loader->document(
        'complex',
        {
            data => [qw(one two three)],
            site => {
                name => 'Test Site'
            }
        }
    );
    
    assert($doc->layout eq 'default');
    
    assert($doc->templateVars('dojo.require')->[0]);
    
    my $text = $doc->Render();
    my $expected = read_file($this->GetResourceFile('Resources', 'TTView.Output', 'complex.default.txt'), binmode => ':utf8' );
    #assert($text eq $expected, '$doc->Render(): Bad output', "Got:      $text", "Expected: $expected"); 
};

test TestDocumentsIsolation => sub {
	my $this = shift;
	
	my $loader = $this->CreateLoader();
    
    my $doc = $loader->document('simple');
    
    assert(ref $loader->context->stash->get([ 'dojo', 0, 'require', 0]) eq 'ARRAY');
    assertarray($loader->context->stash->get([ 'dojo', 0, 'require', 0]),[]);
    assert($loader->context->stash != $doc->stash);
    
    assert(defined $doc);
    
    # only root stash variables can be localized, to avoid modifying dojo we
    # need to replace it completely 
    $doc->context->process(\q{
    	[% SET dojo = { require => [] } %]
    	[% dojo.require.push('dijit/form/TextBox') %]
    	[% SET user = 'dummy guy' %]
    });
    
    assert($doc->context->stash->get('user') eq 'dummy guy');
    assert($loader->context->stash->get('user') eq 'test_user');
    assertarray($doc->context->stash->get([ 'dojo', 0, 'require', 0]),['dijit/form/TextBox']);
    assertarray($loader->context->stash->get([ 'dojo', 0, 'require', 0]),[]);
    
    my $text = $doc->Render();
    
    my $doc2 = $loader->document('simple');
    
    assertarray($doc2->context->stash->get([ 'dojo', 0, 'require', 0]),[]);
};

test TestMemoryLeaks => sub {
    my ($this) = @_;
    
    my $loader = $this->CreateLoader();
    $loader->document('simple'); # force loader initialization
    
    AssertMemoryLeak(sub {        
        my $doc = $loader->document('simple');
    });
        
    AssertMemoryLeak(sub {
        my $doc = $loader->document('simple');
        $doc->Render( { self => $doc } );
    });
    
    $loader->template('Layout/default');
    $loader->template('My/Org/Panel');
    $loader->template('My/Org/TextPreview');
    AssertMemoryLeak(sub {
        my $doc = $loader->document('simple');
        my $factory = $doc->RequireControl('My/Org/Panel');
        my $ctl = $doc->childNodes($factory->new('information', { visualClass => 'complex' }) );        
    },'dump');
    
    $loader->template('complex');
    AssertMemoryLeak(sub {
        my $doc = $loader->document('complex');
        $doc->Render();
    },'dump');
    
};

1;