SVN: ZODB/branches/test_repozo/src/ZODB/scripts/ setup scripts/tests

Godefroid Chapelle <[email protected]>
Newsgroups gmane.comp.python.zope.zodb.cvs
Message-ID <20091202125147.051487083BC__14641.8720287786$1259759212$gmane$org@cvs.zope.org>
Log message for revision 106171:
  setup scripts/tests

Changed:
  D   ZODB/branches/test_repozo/src/ZODB/scripts/fstail.txt
  D   ZODB/branches/test_repozo/src/ZODB/scripts/manual_tests/testrepozo.py
  D   ZODB/branches/test_repozo/src/ZODB/scripts/referrers.txt
  A   ZODB/branches/test_repozo/src/ZODB/scripts/tests/
  A   ZODB/branches/test_repozo/src/ZODB/scripts/tests/__init__.py
  A   ZODB/branches/test_repozo/src/ZODB/scripts/tests/fstail.txt
  A   ZODB/branches/test_repozo/src/ZODB/scripts/tests/referrers.txt
  A   ZODB/branches/test_repozo/src/ZODB/scripts/tests/test_doc.py
  A   ZODB/branches/test_repozo/src/ZODB/scripts/tests/test_repozo.py
  D   ZODB/branches/test_repozo/src/ZODB/scripts/tests.py

-=-
Deleted: ZODB/branches/test_repozo/src/ZODB/scripts/fstail.txt
===================================================================
--- ZODB/branches/test_repozo/src/ZODB/scripts/fstail.txt	2009-12-02 12:49:14 UTC (rev 106170)
+++ ZODB/branches/test_repozo/src/ZODB/scripts/fstail.txt	2009-12-02 12:51:46 UTC (rev 106171)
@@ -1,40 +0,0 @@
-====================
-The `fstail` utility
-====================
-
-The `fstail` utility shows information for a FileStorage about the last `n`
-transactions:
-
-We have to prepare a FileStorage first:
-
-  >>> from ZODB.FileStorage import FileStorage
-  >>> from ZODB.DB import DB
-  >>> import transaction
-  >>> from tempfile import mktemp
-  >>> storagefile = mktemp()
-  >>> base_storage = FileStorage(storagefile)
-  >>> database = DB(base_storage)
-  >>> connection1 = database.open()
-  >>> root = connection1.root()
-  >>> root['foo'] = 1
-  >>> transaction.commit()
-
-Now lets have a look at the last transactions of this FileStorage:
-
-  >>> from ZODB.scripts.fstail import main
-  >>> main(storagefile, 5)
-  2007-11-10 15:18:48.543001: hash=b16422d09fabdb45d4e4325e4b42d7d6f021d3c3
-  user='' description='' length=132 offset=185
-  <BLANKLINE>
-  2007-11-10 15:18:48.543001: hash=b16422d09fabdb45d4e4325e4b42d7d6f021d3c3
-  user='' description='initial database creation' length=150 offset=52
-  <BLANKLINE>
-
-Now clean up the storage again:
-
-  >>> import os
-  >>> base_storage.close()
-  >>> os.unlink(storagefile)
-  >>> os.unlink(storagefile+'.index')
-  >>> os.unlink(storagefile+'.lock')
-  >>> os.unlink(storagefile+'.tmp')

Deleted: ZODB/branches/test_repozo/src/ZODB/scripts/manual_tests/testrepozo.py
===================================================================
--- ZODB/branches/test_repozo/src/ZODB/scripts/manual_tests/testrepozo.py	2009-12-02 12:49:14 UTC (rev 106170)
+++ ZODB/branches/test_repozo/src/ZODB/scripts/manual_tests/testrepozo.py	2009-12-02 12:51:46 UTC (rev 106171)
@@ -1,164 +0,0 @@
-#!/usr/bin/env python
-##############################################################################
-#
-# Copyright (c) 2004 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-
-"""Test repozo.py.
-
-This is a by-hand test.  It succeeds iff it doesn't blow up.  Run it with
-its home directory as the current directory.  It will destroy all files
-matching Data.* and Copy.* in this directory, and anything in a
-subdirectory of name 'backup'.
-
-Usage:
-
-python testrepozo.py [repozo_script]
-
-  repozo_script, if provided, is a path to a script that runs repozo,
-  such as that generated by buildout.
-
-eg:
-$ ../../../../bin/py testrepozo.py ../../../../bin/repozo
-"""
-
-import os
-import random
-import time
-import glob
-import sys
-import shutil
-
-import ZODB
-from ZODB import FileStorage
-import transaction
-
-def cleanup():
-    for fname in glob.glob('Data.*') + glob.glob('Copy.*'):
-        os.remove(fname)
-
-    if os.path.isdir('backup'):
-        for fname in os.listdir('backup'):
-            os.remove(os.path.join('backup', fname))
-        os.rmdir('backup')
-
-class OurDB:
-    def __init__(self):
-        from BTrees.OOBTree import OOBTree
-        self.getdb()
-        conn = self.db.open()
-        conn.root()['tree'] = OOBTree()
-        transaction.commit()
-        self.close()
-
-    def getdb(self):
-        storage = FileStorage.FileStorage('Data.fs')
-        self.db = ZODB.DB(storage)
-
-    def gettree(self):
-        self.getdb()
-        conn = self.db.open()
-        return conn.root()['tree']
-
-    def pack(self):
-        self.getdb()
-        self.db.pack()
-
-    def close(self):
-        if self.db is not None:
-            self.db.close()
-            self.db = None
-
-# Do recovery to time 'when', and check that it's identical to correctpath.
-def check(correctpath='Data.fs', when=None):
-    if when is None:
-        extra = ''
-    else:
-        extra = ' -D ' + when
-    cmd = PYTHON + REPOZO + ' -vRr backup -o Copy.fs' + extra
-    os.system(cmd)
-    f = file(correctpath, 'rb')
-    g = file('Copy.fs', 'rb')
-    fguts = f.read()
-    gguts = g.read()
-    f.close()
-    g.close()
-    if fguts != gguts:
-        raise ValueError("guts don't match\n"
-                         "    correctpath=%r when=%r\n"
-                         "    cmd=%r" % (correctpath, when, cmd))
-
-def mutatedb(db):
-    # Make random mutations to the btree in the database.
-    tree = db.gettree()
-    for dummy in range(100):
-        if random.random() < 0.6:
-            tree[random.randrange(100000)] = random.randrange(100000)
-        else:
-            keys = tree.keys()
-            if keys:
-                del tree[keys[0]]
-    transaction.commit()
-    db.close()
-
-def main():
-    cleanup()
-    os.mkdir('backup')
-    d = OurDB()
-    # Every 9th time thru the loop, we save a full copy of Data.fs,
-    # and at the end we ensure we can reproduce those too.
-    saved_snapshots = []  # list of (name, time) pairs for copies.
-
-    for i in range(100):
-        # Make some mutations.
-        mutatedb(d)
-
-        # Pack about each tenth time.
-        if random.random() < 0.1:
-            print "packing"
-            d.pack()
-            d.close()
-
-        # Make an incremental backup, half the time with gzip (-z).
-        if random.random() < 0.5:
-            os.system(PYTHON + REPOZO + ' -vBQr backup -f Data.fs')
-        else:
-            os.system(PYTHON + REPOZO + ' -zvBQr backup -f Data.fs')
-
-        if i % 9 == 0:
-            copytime = '%04d-%02d-%02d-%02d-%02d-%02d' % (time.gmtime()[:6])
-            copyname = os.path.join('backup', "Data%d" % i) + '.fs'
-            shutil.copyfile('Data.fs', copyname)
-            saved_snapshots.append((copyname, copytime))
-
-        # Make sure the clock moves at least a second.
-        time.sleep(1.01)
-
-        # Verify current Data.fs can be reproduced exactly.
-        check()
-
-    # Verify snapshots can be reproduced exactly.
-    for copyname, copytime in saved_snapshots:
-        print "Checking that", copyname, "at", copytime, "is reproducible."
-        check(copyname, copytime)
-
-    # Tear it all down.
-    cleanup()
-    print 'Test passed!'
-
-if __name__ == '__main__':
-    PYTHON = sys.executable + ' '
-    if len(sys.argv)>1:
-        REPOZO = sys.argv[1]
-    else:
-        REPOZO = '../repozo.py'
-    main()

Deleted: ZODB/branches/test_repozo/src/ZODB/scripts/referrers.txt
===================================================================
--- ZODB/branches/test_repozo/src/ZODB/scripts/referrers.txt	2009-12-02 12:49:14 UTC (rev 106170)
+++ ZODB/branches/test_repozo/src/ZODB/scripts/referrers.txt	2009-12-02 12:51:46 UTC (rev 106171)
@@ -1,43 +0,0 @@
-Getting Object Referrers
-========================
-
-The referrers module provides a way to get object referrers.  It
-provides a referrers method that takes an iterable storage object.  It
-returns a dictionary mapping object ids to lists of referrer object
-versions, which each version is a tuple an object id nd serial
-nummber.
-
-To see how this works, we'll create a small database:
-
-    >>> import transaction
-    >>> from persistent.mapping import PersistentMapping
-    >>> from ZODB.FileStorage import FileStorage
-    >>> from ZODB.DB import DB
-    >>> import os, tempfile
-    >>> dest = tempfile.mkdtemp()
-    >>> fs = FileStorage(os.path.join(dest, 'Data.fs'))
-    >>> db = DB(fs)
-    >>> conn = db.open()
-    >>> conn.root()['a'] = PersistentMapping()
-    >>> conn.root()['b'] = PersistentMapping()
-    >>> transaction.commit()
-    >>> roid = conn.root()._p_oid
-    >>> aoid = conn.root()['a']._p_oid
-    >>> boid = conn.root()['b']._p_oid
-    >>> s1 = conn.root()['b']._p_serial
-
-    >>> conn.root()['a']['b'] = conn.root()['b']
-    >>> transaction.commit()
-    >>> s2 = conn.root()['a']._p_serial
-
-Now we'll get the storage and compute the referrers:
-
-    >>> import ZODB.scripts.referrers
-    >>> referrers = ZODB.scripts.referrers.referrers(fs)
-
-    >>> referrers[boid] == [(roid, s1), (aoid, s2)]
-    True
-
-.. Cleanup
-
-    >>> db.close()

Copied: ZODB/branches/test_repozo/src/ZODB/scripts/tests/fstail.txt (from rev 106164, ZODB/trunk/src/ZODB/scripts/fstail.txt)
===================================================================
--- ZODB/branches/test_repozo/src/ZODB/scripts/tests/fstail.txt	                        (rev 0)
+++ ZODB/branches/test_repozo/src/ZODB/scripts/tests/fstail.txt	2009-12-02 12:51:46 UTC (rev 106171)
@@ -0,0 +1,40 @@
+====================
+The `fstail` utility
+====================
+
+The `fstail` utility shows information for a FileStorage about the last `n`
+transactions:
+
+We have to prepare a FileStorage first:
+
+  >>> from ZODB.FileStorage import FileStorage
+  >>> from ZODB.DB import DB
+  >>> import transaction
+  >>> from tempfile import mktemp
+  >>> storagefile = mktemp()
+  >>> base_storage = FileStorage(storagefile)
+  >>> database = DB(base_storage)
+  >>> connection1 = database.open()
+  >>> root = connection1.root()
+  >>> root['foo'] = 1
+  >>> transaction.commit()
+
+Now lets have a look at the last transactions of this FileStorage:
+
+  >>> from ZODB.scripts.fstail import main
+  >>> main(storagefile, 5)
+  2007-11-10 15:18:48.543001: hash=b16422d09fabdb45d4e4325e4b42d7d6f021d3c3
+  user='' description='' length=132 offset=185
+  <BLANKLINE>
+  2007-11-10 15:18:48.543001: hash=b16422d09fabdb45d4e4325e4b42d7d6f021d3c3
+  user='' description='initial database creation' length=150 offset=52
+  <BLANKLINE>
+
+Now clean up the storage again:
+
+  >>> import os
+  >>> base_storage.close()
+  >>> os.unlink(storagefile)
+  >>> os.unlink(storagefile+'.index')
+  >>> os.unlink(storagefile+'.lock')
+  >>> os.unlink(storagefile+'.tmp')

Copied: ZODB/branches/test_repozo/src/ZODB/scripts/tests/referrers.txt (from rev 106164, ZODB/trunk/src/ZODB/scripts/referrers.txt)
===================================================================
--- ZODB/branches/test_repozo/src/ZODB/scripts/tests/referrers.txt	                        (rev 0)
+++ ZODB/branches/test_repozo/src/ZODB/scripts/tests/referrers.txt	2009-12-02 12:51:46 UTC (rev 106171)
@@ -0,0 +1,43 @@
+Getting Object Referrers
+========================
+
+The referrers module provides a way to get object referrers.  It
+provides a referrers method that takes an iterable storage object.  It
+returns a dictionary mapping object ids to lists of referrer object
+versions, which each version is a tuple an object id nd serial
+nummber.
+
+To see how this works, we'll create a small database:
+
+    >>> import transaction
+    >>> from persistent.mapping import PersistentMapping
+    >>> from ZODB.FileStorage import FileStorage
+    >>> from ZODB.DB import DB
+    >>> import os, tempfile
+    >>> dest = tempfile.mkdtemp()
+    >>> fs = FileStorage(os.path.join(dest, 'Data.fs'))
+    >>> db = DB(fs)
+    >>> conn = db.open()
+    >>> conn.root()['a'] = PersistentMapping()
+    >>> conn.root()['b'] = PersistentMapping()
+    >>> transaction.commit()
+    >>> roid = conn.root()._p_oid
+    >>> aoid = conn.root()['a']._p_oid
+    >>> boid = conn.root()['b']._p_oid
+    >>> s1 = conn.root()['b']._p_serial
+
+    >>> conn.root()['a']['b'] = conn.root()['b']
+    >>> transaction.commit()
+    >>> s2 = conn.root()['a']._p_serial
+
+Now we'll get the storage and compute the referrers:
+
+    >>> import ZODB.scripts.referrers
+    >>> referrers = ZODB.scripts.referrers.referrers(fs)
+
+    >>> referrers[boid] == [(roid, s1), (aoid, s2)]
+    True
+
+.. Cleanup
+
+    >>> db.close()

Copied: ZODB/branches/test_repozo/src/ZODB/scripts/tests/test_doc.py (from rev 106164, ZODB/trunk/src/ZODB/scripts/tests.py)
===================================================================
--- ZODB/branches/test_repozo/src/ZODB/scripts/tests/test_doc.py	                        (rev 0)
+++ ZODB/branches/test_repozo/src/ZODB/scripts/tests/test_doc.py	2009-12-02 12:51:46 UTC (rev 106171)
@@ -0,0 +1,35 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Test harness for scripts.
+
+$Id$
+"""
+import unittest
+import re
+from zope.testing import doctest, renormalizing
+import ZODB.tests.util
+
+checker = renormalizing.RENormalizing([
+    (re.compile('[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]+'),
+     '2007-11-10 15:18:48.543001'),
+    (re.compile('hash=[0-9a-f]{40}'),
+     'hash=b16422d09fabdb45d4e4325e4b42d7d6f021d3c3')])
+
+def test_suite():
+    return unittest.TestSuite((
+        doctest.DocFileSuite(
+            'referrers.txt', 'fstail.txt',
+            setUp=ZODB.tests.util.setUp, tearDown=ZODB.tests.util.tearDown,
+            checker=checker),
+        ))

Copied: ZODB/branches/test_repozo/src/ZODB/scripts/tests/test_repozo.py (from rev 106164, ZODB/trunk/src/ZODB/scripts/manual_tests/testrepozo.py)
===================================================================
--- ZODB/branches/test_repozo/src/ZODB/scripts/tests/test_repozo.py	                        (rev 0)
+++ ZODB/branches/test_repozo/src/ZODB/scripts/tests/test_repozo.py	2009-12-02 12:51:46 UTC (rev 106171)
@@ -0,0 +1,179 @@
+#!/usr/bin/env python
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+"""Test repozo.py.
+
+This is a by-hand test.  It succeeds iff it doesn't blow up.  Run it with
+its home directory as the current directory.  It will destroy all files
+matching Data.* and Copy.* in this directory, and anything in a
+subdirectory of name 'backup'.
+
+Usage:
+
+python testrepozo.py [repozo_script]
+
+  repozo_script, if provided, is a path to a script that runs repozo,
+  such as that generated by buildout.
+
+eg:
+$ ../../../../bin/py testrepozo.py ../../../../bin/repozo
+"""
+
+import unittest
+import os
+import random
+import time
+import glob
+import shutil
+
+import ZODB
+from ZODB import FileStorage
+import transaction
+
+def cleanup():
+    for fname in glob.glob('Data.*') + glob.glob('Copy.*'):
+        os.remove(fname)
+
+    if os.path.isdir('backup'):
+        for fname in os.listdir('backup'):
+            os.remove(os.path.join('backup', fname))
+        os.rmdir('backup')
+
+class OurDB:
+    def __init__(self):
+        from BTrees.OOBTree import OOBTree
+        self.getdb()
+        conn = self.db.open()
+        conn.root()['tree'] = OOBTree()
+        transaction.commit()
+        self.close()
+
+    def getdb(self):
+        storage = FileStorage.FileStorage('Data.fs')
+        self.db = ZODB.DB(storage)
+
+    def gettree(self):
+        self.getdb()
+        conn = self.db.open()
+        return conn.root()['tree']
+
+    def pack(self):
+        self.getdb()
+        self.db.pack()
+
+    def close(self):
+        if self.db is not None:
+            self.db.close()
+            self.db = None
+
+# Do recovery to time 'when', and check that it's identical to correctpath.
+def check(correctpath='Data.fs', when=None):
+    if when is None:
+        extra = ''
+    else:
+        extra = ' -D ' + when
+    cmd = PYTHON + REPOZO + ' -vRr backup -o Copy.fs' + extra
+    os.system(cmd)
+    f = file(correctpath, 'rb')
+    g = file('Copy.fs', 'rb')
+    fguts = f.read()
+    gguts = g.read()
+    f.close()
+    g.close()
+    if fguts != gguts:
+        raise ValueError("guts don't match\n"
+                         "    correctpath=%r when=%r\n"
+                         "    cmd=%r" % (correctpath, when, cmd))
+
+def mutatedb(db):
+    # Make random mutations to the btree in the database.
+    tree = db.gettree()
+    for dummy in range(100):
+        if random.random() < 0.6:
+            tree[random.randrange(100000)] = random.randrange(100000)
+        else:
+            keys = tree.keys()
+            if keys:
+                del tree[keys[0]]
+    transaction.commit()
+    db.close()
+
+def main():
+    cleanup()
+    os.mkdir('backup')
+    d = OurDB()
+    # Every 9th time thru the loop, we save a full copy of Data.fs,
+    # and at the end we ensure we can reproduce those too.
+    saved_snapshots = []  # list of (name, time) pairs for copies.
+
+    for i in range(100):
+        # Make some mutations.
+        mutatedb(d)
+
+        # Pack about each tenth time.
+        if random.random() < 0.1:
+            print "packing"
+            d.pack()
+            d.close()
+
+        # Make an incremental backup, half the time with gzip (-z).
+        if random.random() < 0.5:
+            os.system(PYTHON + REPOZO + ' -vBQr backup -f Data.fs')
+        else:
+            os.system(PYTHON + REPOZO + ' -zvBQr backup -f Data.fs')
+
+        if i % 9 == 0:
+            copytime = '%04d-%02d-%02d-%02d-%02d-%02d' % (time.gmtime()[:6])
+            copyname = os.path.join('backup', "Data%d" % i) + '.fs'
+            shutil.copyfile('Data.fs', copyname)
+            saved_snapshots.append((copyname, copytime))
+
+        # Make sure the clock moves at least a second.
+        time.sleep(1.01)
+
+        # Verify current Data.fs can be reproduced exactly.
+        check()
+
+    # Verify snapshots can be reproduced exactly.
+    for copyname, copytime in saved_snapshots:
+        print "Checking that", copyname, "at", copytime, "is reproducible."
+        check(copyname, copytime)
+
+    # Tear it all down.
+    cleanup()
+    print 'Test passed!'
+
+
+
+class RepozoTest(unittest.TestCase):
+
+    def setUp(self):
+        pass
+
+    def tearDown(self):
+        pass
+
+    def testDummy(self):
+        self.failUnless(True)
+
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(RepozoTest))
+    return suite
+
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')

Deleted: ZODB/branches/test_repozo/src/ZODB/scripts/tests.py
===================================================================
--- ZODB/branches/test_repozo/src/ZODB/scripts/tests.py	2009-12-02 12:49:14 UTC (rev 106170)
+++ ZODB/branches/test_repozo/src/ZODB/scripts/tests.py	2009-12-02 12:51:46 UTC (rev 106171)
@@ -1,35 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2004 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""Test harness for scripts.
-
-$Id$
-"""
-import unittest
-import re
-from zope.testing import doctest, renormalizing
-import ZODB.tests.util
-
-checker = renormalizing.RENormalizing([
-    (re.compile('[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]+'),
-     '2007-11-10 15:18:48.543001'),
-    (re.compile('hash=[0-9a-f]{40}'),
-     'hash=b16422d09fabdb45d4e4325e4b42d7d6f021d3c3')])
-
-def test_suite():
-    return unittest.TestSuite((
-        doctest.DocFileSuite(
-            'referrers.txt', 'fstail.txt',
-            setUp=ZODB.tests.util.setUp, tearDown=ZODB.tests.util.tearDown,
-            checker=checker),
-        ))
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.