 +     */
 +    public function __destruct() {
 +      $this->finalize();
++    }
+   }
+ ?>
+Index: skeleton/util/log/LogAppender.class.php
+===================================================================
+--- skeleton/util/log/LogAppender.class.php	(revision 13884)
++++ skeleton/util/log/LogAppender.class.php	(working copy)
+@@ -11,29 +11,49 @@
+    * @purpose  Base class
+    */
+   abstract class LogAppender extends Object {
++    protected $layout= NULL;
+ 
+     /**
++     * Sets layout
++     *
++     * @param   util.log.Layout layout
++     */
++    public function setLayout(utillogLayout $layout) {
++      $this->layout= $layout;
++    }
++    
++    /**
++     * Sets layout and returns this appender
++     *
++     * @param   util.log.Layout layout
++     * @return  util.log.LogAppender
++     */
++    public function withLayout(utillogLayout $layout) {
++      $this->layout= $layout;
++      return $this;
++    }
++
++    /**
++     * Gets layout
++     *
++     * @return  util.log.Layout
++     */
++    public function getLayout() {
++      return $this->layout;
++    }
++
++    /**
+      * Append data
+      *
+-     * @param   mixed* args
++     * @param   util.log.LoggingEvent event
+      */ 
+-    public abstract function append();
+- 
++    public abstract function append(LoggingEvent $event);
++    
+     /**
+      * Finalize this appender. This method is called when the logger
+      * is shut down. Does nothing in this default implementation.
+      *
+      */   
+     public function finalize() { }
+-    
+-    /**
+-     * Retrieve a readable representation of a variable
+-     *
+-     * @param   mixed var
+-     * @return  string
+-     */
+-    protected function varSource($var) {
+-      return is_string($var) ? $var : xp::stringOf($var);
+-    }
+   }
+ ?>
+Index: skeleton/util/log/BufferedAppender.class.php
+===================================================================
+--- skeleton/util/log/BufferedAppender.class.php	(revision 13884)
++++ skeleton/util/log/BufferedAppender.class.php	(working copy)
+@@ -13,19 +13,15 @@
+    * @purpose  Appender
+    */  
+   class BufferedAppender extends LogAppender {
+-    public 
+-      $buffer = '';
++    public $buffer= '';
+ 
+     /**
+-     * Appends log data to the buffer
++     * Append data
+      *
+-     * @param  mixed* args variables
+-     */
+-    public function append() {
+-      foreach (func_get_args() as $arg) {
+-        $this->buffer.= $this->varSource($arg).' ';
+-      }
+-      $this->buffer.= "\n";
++     * @param   util.log.LoggingEvent event
++     */ 
++    public function append(LoggingEvent $event) {
++      $this->buffer.= $this->layout->format($event);
+     }
+     
+     /**
+Index: skeleton/util/log/SmtpAppender.class.php
+===================================================================
+--- skeleton/util/log/SmtpAppender.class.php	(revision 13884)
++++ skeleton/util/log/SmtpAppender.class.php	(working copy)
+@@ -43,24 +43,18 @@
+     }
+     
+     /**
+-     * Sends log data to the specified email address
++     * Append data
+      *
+-     * @param   mixed args variables
+-     */
+-    public function append() {
+-      $body= '';
+-      
+-      with ($args= func_get_args()); {
+-        foreach ($args as $idx => $arg) {
+-          $body.= $this->varSource($arg).($idx < sizeof($args)-1 ? ' ' : '');
+-        }
+-      }
+-      
++     * @param   util.log.LoggingEvent event
++     */ 
++    public function append(LoggingEvent $event) {
++      $body= $this->layout->format($event);
+       if ($this->sync) {
+         mail($this->email, $this->prefix, $body);
+       } else {
+         $this->_data[]= $body;
+       }
++
+     }
+     
+     /**
+Index: skeleton/util/log/LogLevel.class.php
+===================================================================
+--- skeleton/util/log/LogLevel.class.php	(revision 13884)
++++ skeleton/util/log/LogLevel.class.php	(working copy)
+@@ -4,6 +4,12 @@
+  * $Id$ 
+  */
+ 
++  define('LOGGER_FLAG_INFO',    0x0001);
++  define('LOGGER_FLAG_WARN',    0x0002);
++  define('LOGGER_FLAG_ERROR',   0x0004);
++  define('LOGGER_FLAG_DEBUG',   0x0008);
++  define('LOGGER_FLAG_ALL',     LOGGER_FLAG_INFO | LOGGER_FLAG_WARN | LOGGER_FLAG_ERROR | LOGGER_FLAG_DEBUG);
++
+   /**
+    * Log levels
+    *
+@@ -45,5 +51,26 @@
+       }
+       return $map[$key];
+     }
++
++    /**
++     * Retrieve a loglevel name for a given level
++     *
++     * @param   int level
++     * @return  string
++     * @throws  lang.IllegalArgumentException
++     */
++    public static function nameOf($level) {
++      static $map= array(
++        self::INFO  => 'INFO',
++        self::WARN  => 'WARN',
++        self::ERROR => 'ERROR',
++        self::DEBUG => 'DEBUG',
++      );
++    
++      if (!isset($map[$level])) {
++        throw new IllegalArgumentException('No such loglevel '.$level);
++      }
++      return $map[$level];
++    }
+   }
+ ?>
+Index: skeleton/util/log/StreamAppender.class.php
+===================================================================
+--- skeleton/util/log/StreamAppender.class.php	(revision 13884)
++++ skeleton/util/log/StreamAppender.class.php	(working copy)
+@@ -13,30 +13,24 @@
+    * @purpose  Appender
+    */  
+   class StreamAppender extends LogAppender {
+-    public 
+-      $stream = NULL;
++    public $stream= NULL;
+     
+     /**
+      * Constructor
+      *
+      * @param   io.streams.OutputStream stream
+      */
+-    public function __construct($stream) {
++    public function __construct(OutputStream $stream) {
+       $this->stream= $stream;
+     }
+     
+     /**
+-     * Appends log data to the file
++     * Append data
+      *
+-     * @param   mixed* args variables
+-     */
+-    public function append() {
+-      with ($args= func_get_args()); {
+-        foreach ($args as $idx => $arg) {
+-          $this->stream->write($this->varSource($arg). ($idx < sizeof($args)-1 ? ' ' : ''));
+-        }
+-      }
+-      $this->stream->write("\n");
++     * @param   util.log.LoggingEvent event
++     */ 
++    public function append(LoggingEvent $event) {
++      $this->stream->write($this->layout->format($event));
+     }
+   }
+ ?>
+Index: skeleton/util/log/PatternLayout.class.php
+===================================================================
+--- skeleton/util/log/PatternLayout.class.php	(revision 0)
++++ skeleton/util/log/PatternLayout.class.php	(revision 0)
+@@ -0,0 +1,93 @@
++<?php
++/* This class is part of the XP framework
++ *
++ * $Id$ 
++ */
++
++  uses('util.log.Layout');
++
++  /**
++   * Pattern layout
++   *
++   * Format string
++   * -------------
++   * The format string consists of format tokens preceded by a percent
++   * sign (%) and any other character. The following format tokens are 
++   * supported:
++   * <ul>
++   *   <li>%m - Message</li>
++   *   <li>%c - Category name</li>
++   *   <li>%l - Log level - lowercase</li>
++   *   <li>%L - Log level - uppercase</li>
++   *   <li>%t - Time in HH:MM:SS</li>
++   *   <li>%p - Process ID</li>
++   *   <li>%% - A literal percent sign (%)</li>
++   * </li>
++   *
++   * @test    xp://net.xp_framework.unittest.logging.PatternLayoutTest
++   */
++  class PatternLayout extends utillogLayout {
++    protected $format= array();
++  
++    /**
++     * Creates a new pattern layout
++     *
++     * @param   string format
++     */
++    public function __construct($format) {
++      for ($i= 0, $s= strlen($format); $i < $s; $i++) {
++        if ('%' === $format{$i}) {
++          $i++;
++          switch ($format{$i}) {
++            case '%': {   // Literal percent
++              $this->format[]= '%'; 
++              break;
++            }
++            default: {    // Any other character - verify it's supported
++              if (!strspn($format{$i}, 'mclLtp')) {
++                throw new IllegalArgumentException('Unknown format token "'.$format{$i}.'"');
++              }
++              $this->format[]= '%'.$format{$i};
++            }
++          }
++        } else {
++          $this->format[]= $format{$i};
++        }
++      }
++    }
++
++    /**
++     * Creates a string representation of the given argument. For any 
++     * string given, the result is the string itself, for any other type,
++     * the result is the xp::stringOf() output.
++     *
++     * @param   var arg
++     * @return  string
++     */
++    protected function stringOf($arg) {
++      return is_string($arg) ? $arg : xp::stringOf($arg);
++    }
++
++    /**
++     * Formats a logging event according to this layout
++     *
++     * @param   util.log.LoggingEvent event
++     * @return  string
++     */
++    public function format(LoggingEvent $event) {
++      $out= '';
++      foreach ($this->format as $token) {
++        switch ($token) {
++          case '%m': $out.= implode(' ', array_map(array($this, 'stringOf'), $event->getArguments())); break;
++          case '%t': $out.= gmdate('H:i:s', $event->getTimestamp()); break;
++          case '%c': $out.= $event->getCategory()->identifier; break;
++          case '%l': $out.= strtolower(LogLevel::nameOf($event->getLevel())); break;
++          case '%L': $out.= strtoupper(LogLevel::nameOf($event->getLevel())); break;
++          case '%p': $out.= $event->getProcessId(); break;
++          default: $out.= $token;
++        }
++      }
++      return $out;
++    }
++  }
++?>
+
+Property changes on: skeleton/util/log/PatternLayout.class.php
+___________________________________________________________________
+Added: svn:keywords
+   + Id
+
+Index: skeleton/util/log/Layout.class.php
+===================================================================
+--- skeleton/util/log/Layout.class.php	(revision 0)
++++ skeleton/util/log/Layout.class.php	(revision 0)
+@@ -0,0 +1,25 @@
++<?php
++/* This class is part of the XP framework
++ *
++ * $Id$ 
++ */
++
++  $package= 'util.log';
++
++  uses('util.log.LoggingEvent');
++
++  /**
++   * Takes care of formatting log entries
++   *
++   */
++  abstract class utillogLayout extends Object {
++    
++    /**
++     * Formats a logging event according to this layout
++     *
++     * @param   util.log.LoggingEvent event
++     * @return  string
++     */
++    public abstract function format(LoggingEvent $event);
++  }
++?>
+
+Property changes on: skeleton/util/log/Layout.class.php
+___________________________________________________________________
+Added: svn:keywords
+   + Id
+
+Index: skeleton/util/log/LogCategory.class.php
+===================================================================
+--- skeleton/util/log/LogCategory.class.php	(revision 13884)
++++ skeleton/util/log/LogCategory.class.php	(working copy)
+@@ -4,14 +4,8 @@
+  * $Id$
+  */
+ 
+-  uses('util.log.LogLevel');
++  uses('util.log.LogLevel', 'util.log.LoggingEvent', 'util.log.DefaultLayout');
+ 
+-  define('LOGGER_FLAG_INFO',    0x0001);
+-  define('LOGGER_FLAG_WARN',    0x0002);
+-  define('LOGGER_FLAG_ERROR',   0x0004);
+-  define('LOGGER_FLAG_DEBUG',   0x0008);
+-  define('LOGGER_FLAG_ALL',     LOGGER_FLAG_INFO | LOGGER_FLAG_WARN | LOGGER_FLAG_ERROR | LOGGER_FLAG_DEBUG);
+-
+   /**
+    * The log category is the interface to be used. All logging information
+    * is sent to a log category via one of the info, warn, error, debug 
+@@ -40,36 +34,26 @@
+    * @purpose  Base class
+    */
+   class LogCategory extends Object {
+-    public 
+-      $_appenders= array(),
+-      $_indicators= array(
+-        LogLevel::INFO        => 'info',
+-        LogLevel::WARN        => 'warn',
+-        LogLevel::ERROR       => 'error',
+-        LogLevel::DEBUG       => 'debug'
+-      );
++    protected static $DEFAULT_LAYOUT= NULL;
++    protected $_appenders= array();
++
++    public $flags= 0;
++    public $identifier= '';
+       
+-    public
+-      $flags,
+-      $identifier,
+-      $dateformat,
+-      $format;
++    static function __static() {
++      self::$DEFAULT_LAYOUT= new DefaultLayout();
++    }
+ 
+     /**
+      * Constructor
+      *
+      * @param   string identifier
+-     * @param   string format 
+-     * @param   string dateformat
+-     * @param   int flags
++     * @param   int flags (defaults to all)
+      */
+-    public function __construct($identifier, $format, $dateformat, $flags= LogLevel::ALL) {
+-      $this->identifier= $identifier;
+-      $this->format= $format;
+-      $this->dateformat= $dateformat;
++    public function __construct($identifier, $flags= LogLevel::ALL) {
+       $this->flags= $flags;
++      $this->identifier= $identifier;
+       $this->_appenders= array();
+-      
+     }
+ 
+     /**
+@@ -92,28 +76,15 @@
+     }
+     
+     /**
+-     * Private helper function
++     * Calls all appenders
+      *
+      */
+-    public function callAppenders() {
+-      $args= func_get_args();
+-      $flag= $args[0];
+-      if (!($this->flags & $flag)) return;
+-      
+-      $args[0]= sprintf(
+-        $this->format,
+-        date($this->dateformat),
+-        $this->identifier,
+-        $this->_indicators[$flag]
+-      );
+-      
++    protected function callAppenders($event) {
++      $level= $event->getLevel();
+       foreach (array_keys($this->_appenders) as $appflag) {
+-        if (!($flag & $appflag)) continue;
+-        foreach (array_keys($this->_appenders[$appflag]) as $idx) {
+-          call_user_func_array(
+-            array($this->_appenders[$appflag][$idx], 'append'),
+-            $args
+-          );
++        if (!($level & $appflag)) continue;
++        foreach ($this->_appenders[$appflag] as $appender) {
++          $appender->append($event);
+         }
+       }
+     }
+@@ -133,8 +104,8 @@
+      */
+     public function finalize() {
+       foreach ($this->_appenders as $flags => $appenders) {
+-        foreach (array_keys($appenders) as $idx) {
+-          $appenders[$idx]->finalize();
++        foreach ($this->_appenders[$appflag] as $appender) {
++          $appender->finalize();
+         }
+       }
+     }
+@@ -150,6 +121,7 @@
+      */
+     public function addAppender($appender, $flag= LogLevel::ALL) {
+       $this->_appenders[$flag][]= $appender;
++      $appender->getLayout() || $appender->setLayout(self::$DEFAULT_LAYOUT);
+       return $appender;
+     }
+ 
+@@ -165,6 +137,7 @@
+      */
+     public function withAppender($appender, $flag= LogLevel::ALL) {
+       $this->_appenders[$flag][]= $appender;
++      $appender->getLayout() || $appender->setLayout(self::$DEFAULT_LAYOUT);
+       return $this;
+     }
+     
+@@ -211,9 +184,9 @@
+      * @param   mixed* args
+      */
+     public function info() {
++      if (!($this->flags & LogLevel::INFO)) return;
+       $args= func_get_args();
+-      array_unshift($args, LogLevel::INFO);
+-      call_user_func_array(array($this, 'callAppenders'), $args);
++      $this->callAppenders(new LoggingEvent($this, time(), getmypid(), LogLevel::INFO, $args));
+     }
+ 
+     /**
+@@ -228,8 +201,9 @@
+      * @param   mixed* args
+      */
+     public function infof() {
++      if (!($this->flags & LogLevel::INFO)) return;
+       $args= func_get_args();
+-      $this->callAppenders(LogLevel::INFO, vsprintf($args[0], array_slice($args, 1)));
++      $this->callAppenders(new LoggingEvent($this, time(), getmypid(), LogLevel::INFO, array(vsprintf($args[0], array_slice($args, 1)))));
+     }
+ 
+     /**
+@@ -238,9 +212,9 @@
+      * @param   mixed* args
+      */
+     public function warn() {
++      if (!($this->flags & LogLevel::WARN)) return;
+       $args= func_get_args();
+-      array_unshift($args, LogLevel::WARN);
+-      call_user_func_array(array($this, 'callAppenders'), $args);
++      $this->callAppenders(new LoggingEvent($this, time(), getmypid(), LogLevel::WARN, $args));
+     }
+ 
+     /**
+@@ -250,8 +224,9 @@
+      * @param   mixed* args
+      */
+     public function warnf() {
++      if (!($this->flags & LogLevel::WARN)) return;
+       $args= func_get_args();
+-      $this->callAppenders(LogLevel::WARN, vsprintf($args[0], array_slice($args, 1)));
++      $this->callAppenders(new LoggingEvent($this, time(), getmypid(), LogLevel::WARN, array(vsprintf($args[0], array_slice($args, 1)))));
+     }
+ 
+     /**
+@@ -260,9 +235,9 @@
+      * @param   mixed* args
+      */
+     public function error() {
++      if (!($this->flags & LogLevel::ERROR)) return;
+       $args= func_get_args();
+-      array_unshift($args, LogLevel::ERROR);
+-      call_user_func_array(array($this, 'callAppenders'), $args);
++      $this->callAppenders(new LoggingEvent($this, time(), getmypid(), LogLevel::ERROR, $args));
+     }
+ 
+     /**
+@@ -272,8 +247,9 @@
+      * @param   mixed* args
+      */
+     public function errorf() {
++      if (!($this->flags & LogLevel::ERROR)) return;
+       $args= func_get_args();
+-      $this->callAppenders(LogLevel::ERROR, vsprintf($args[0], array_slice($args, 1)));
++      $this->callAppenders(new LoggingEvent($this, time(), getmypid(), LogLevel::ERROR, array(vsprintf($args[0], array_slice($args, 1)))));
+     }
+ 
+     /**
+@@ -282,9 +258,9 @@
+      * @param   mixed* args
+      */
+     public function debug() {
++      if (!($this->flags & LogLevel::DEBUG)) return;
+       $args= func_get_args();
+-      array_unshift($args, LogLevel::DEBUG);
+-      call_user_func_array(array($this, 'callAppenders'), $args);
++      $this->callAppenders(new LoggingEvent($this, time(), getmypid(), LogLevel::DEBUG, $args));
+     }
+  
+     /**
+@@ -294,8 +270,9 @@
+      * @param   mixed* args
+      */
+     public function debugf() {
++      if (!($this->flags & LogLevel::DEBUG)) return;
+       $args= func_get_args();
+-      $this->callAppenders(LogLevel::DEBUG, vsprintf($args[0], array_slice($args, 1)));
++      $this->callAppenders(new LoggingEvent($this, time(), getmypid(), LogLevel::DEBUG, array(vsprintf($args[0], array_slice($args, 1)))));
+     }
+    
+     /**
+@@ -303,7 +280,20 @@
+      *
+      */
+     public function mark() {
+-      $this->callAppenders(LogLevel::INFO, str_repeat('-', 72));
++      $this->callAppenders(new LoggingEvent($this, time(), getmypid(), LogLevel::INFO, array(str_repeat('-', 72))));
+     }
++
++    /**
++     * Returns whether another object is equal to this
++     *
++     * @param   lang.Generic cmp
++     * @return  bool
++     */
++    public function equals($cmp) {
++      return (
++        $cmp instanceof self &&
++        $cmp->flags === $this->flags
++      );
 +    }
    }
  ?>

Index: skeleton/util/log/ColoredConsoleAppender.class.php
===================================================================
--- skeleton/util/log/ColoredConsoleAppender.class.php	(revision 13884)

     }
   }
 ?>
Index: skeleton/util/log/DefaultLayout.class.php
===================================================================
--- skeleton/util/log/DefaultLayout.class.php	(revision 0)
+++ skeleton/util/log/DefaultLayout.class.php	(revision 0)
@@ -0,0 +1,43 @@
+<?php
+/* This class is part of the XP framework
+ *
+ * $Id$ 
+ */
+
+  uses('util.log.Layout');
+
+  /**
+   * Default layout
+   *
+   */
+  class DefaultLayout extends utillogLayout {
+  
+    /**
+     * Creates a string representation of the given argument. For any 
+     * string given, the result is the string itself, for any other type,
+     * the result is the xp::stringOf() output.
+     *
+     * @param   var arg
+     * @return  string
+     */
+    protected function stringOf($arg) {
+      return is_string($arg) ? $arg : xp::stringOf($arg);
+    }
+
+    /**
+     * Formats a logging event according to this layout
+     *
+     * @param   util.log.LoggingEvent event
+     * @return  string
+     */
+    public function format(LoggingEvent $event) {
+      return sprintf(
+        '[%s %5d %5s] %s', 
+        date('H:i:s', $event->getTimestamp()),
+        $event->getProcessId(),
+        strtolower(LogLevel::nameOf($event->getLevel())),
+        implode(' ', array_map(array($this, 'stringOf'), $event->getArguments()))
+      );
+    }
+  }
+?>

Property changes on: skeleton/util/log/DefaultLayout.class.php
___________________________________________________________________
Added: svn:keywords
   + Id

Index: ports/classes/net/xp_framework/unittest/logging/LoggingEventTest.class.php
===================================================================
--- ports/classes/net/xp_framework/unittest/logging/LoggingEventTest.class.php	(revision 0)
+++ ports/classes/net/xp_framework/unittest/logging/LoggingEventTest.class.php	(revision 0)
@@ -0,0 +1,79 @@
+<?php
+/* This class is part of the XP framework
+ *
+ * $Id$ 
+ */
+
+  uses(
+    'unittest.TestCase',
+    'util.log.LoggingEvent'
+  );
+
+  /**
+   * TestCase
+   *
+   * @see      xp://util.log.LoggingEvent
+   */
+  class LoggingEventTest extends TestCase {
+    protected $fixture= NULL;
+  
+    /**
+     * Creates fixture
+     *
+     */
+    public function setUp() {
+      $this->fixture= new LoggingEvent(
+        new LogCategory('default', NULL, NULL, 0), 
+        1258733284, 
+        1, 
+        LogLevel::INFO, 
+        array('Hello')
+      );
+    }
+  
+    /**
+     * Test getCategory() method
+     *
+     */
+    #[@test]
+    public function getCategory() {
+      $this->assertEquals(new LogCategory('default', NULL, NULL, 0), $this->fixture->getCategory());
+    }
+ 
+    /**
+     * Test getTimestamp() method
+     *
+     */
+    #[@test]
+    public function getTimestamp() {
+      $this->assertEquals(1258733284, $this->fixture->getTimestamp());
+    }
+
+    /**
+     * Test getProcessId() method
+     *
+     */
+    #[@test]
+    public function getProcessId() {
+      $this->assertEquals(1, $this->fixture->getProcessId());
+    }
+
+    /**
+     * Test getLevel() method
+     *
+     */
+    #[@test]
+    public function getLevel() {
+      $this->assertEquals(LogLevel::INFO, $this->fixture->getLevel());
+    }
+
+    /**
+     * Test getArguments() method
+     *
+     */
+    #[@test]
+    public function getArguments() {
+      $this->assertEquals(array('Hello'), $this->fixture->getArguments());
+    }
+  }
+?>

Property changes on: ports/classes/net/xp_framework/unittest/logging/LoggingEventTest.class.php
___________________________________________________________________
Added: svn:keywords
   + Id

Index: ports/classes/net/xp_framework/unittest/logging/PatternLayoutTest.class.php
===================================================================
--- ports/classes/net/xp_framework/unittest/logging/PatternLayoutTest.class.php	(revision 0)
+++ ports/classes/net/xp_framework/unittest/logging/PatternLayoutTest.class.php	(revision 0)
@@ -0,0 +1,92 @@
+<?php
+/* This class is part of the XP framework
+ *
+ * $Id$ 
+ */
+
+  uses(
+    'unittest.TestCase',
+    'util.log.PatternLayout'
+  );
+
+  /**
+   * TestCase
+   *
+   * @see      xp://util.log.PatternLayout
+   */
+  class PatternLayoutTest extends TestCase {
+
+    /**
+     * Test illegal format token %Q
+     * 
+     */
+    #[@test, @expect('lang.IllegalArgumentException')]
+    public function illegalFormatToken() {
+      new PatternLayout('%Q');
+    }
+ 
+    /**
+     * Test unterminated format token
+     * 
+     */
+    #[@test, @expect('lang.IllegalArgumentException')]
+    public function unterminatedFormatToken() {
+      new PatternLayout('%');
+    }
+    
+    /**
+     * Creates a new logging event
+     *
+     * @return  util.log.LoggingEvent
+     */
+    protected function newLoggingEvent() {
+      return new LoggingEvent(
+        new LogCategory('default'), 
+        1258733284, 
+        1214, 
+        LogLevel::WARN, 
+        array('Hello')
+      );   
+    }
+
+    /**
+     * Test literal percent
+     * 
+     */
+    #[@test]
+    public function literalPercent() {
+      $this->assertEquals(
+        '100%',
+        create(new PatternLayout('100%%'))->format($this->newLoggingEvent())
+      );
+    }
+
+    /**
+     * Test simple format:
+     * <pre>
+     *   INFO [default] Hello
+     * </pre>
+     */
+    #[@test]
+    public function simpleFormat() {
+      $this->assertEquals(
+        'WARN [default] Hello',
+        create(new PatternLayout('%L [%c] %m'))->format($this->newLoggingEvent())
+      );
+    }
+
+    /**
+     * Test default format:
+     * <pre>
+     *   [16:08:04 1214 warn] Hello
+     * </pre>
+     */
+    #[@test]
+    public function defaultFormat() {
+      $this->assertEquals(
+        '[16:08:04 1214 warn] Hello',
+        create(new PatternLayout('[%t %p %l] %m'))->format($this->newLoggingEvent())
+      );
+    }
+  }
+?>

Property changes on: ports/classes/net/xp_framework/unittest/logging/PatternLayoutTest.class.php
___________________________________________________________________
Added: svn:keywords
   + Id

Index: ports/classes/net/xp_framework/unittest/logging/LogCategoryTest.class.php
===================================================================
--- ports/classes/net/xp_framework/unittest/logging/LogCategoryTest.class.php	(revision 13884)
