[MERGE] Merged with main server
bzr revid: tde@openerp.com-20120308083904-l05slz6td2ibggk4
This commit is contained in:
commit
071dc54a5c
42
doc/Makefile
42
doc/Makefile
|
@ -2,17 +2,19 @@
|
|||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXOPTS = -q
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = build
|
||||
BUILDDIR = _build
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
# the i18n builder cannot share the environment and doctrees with the others
|
||||
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
|
||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
|
||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
|
@ -29,6 +31,9 @@ help:
|
|||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " text to make text files"
|
||||
@echo " man to make manual pages"
|
||||
@echo " texinfo to make Texinfo files"
|
||||
@echo " info to make Texinfo files and run them through makeinfo"
|
||||
@echo " gettext to make PO message catalogs"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
|
@ -43,6 +48,7 @@ html:
|
|||
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
sed -i '/-99999/d' _build/dirhtml/_static/flasky.css
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
|
@ -72,17 +78,17 @@ qthelp:
|
|||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/OpenERPWeb.qhcp"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/OpenERPTechnicalDocumentation.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/OpenERPWeb.qhc"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/OpenERPTechnicalDocumentation.qhc"
|
||||
|
||||
devhelp:
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/OpenERPWeb"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/OpenERPWeb"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/OpenERPTechnicalDocumentation"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/OpenERPTechnicalDocumentation"
|
||||
@echo "# devhelp"
|
||||
|
||||
epub:
|
||||
|
@ -100,7 +106,7 @@ latex:
|
|||
latexpdf:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
make -C $(BUILDDIR)/latex all-pdf
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
text:
|
||||
|
@ -113,6 +119,24 @@ man:
|
|||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
texinfo:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo
|
||||
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
||||
@echo "Run \`make' in that directory to run these through makeinfo" \
|
||||
"(use \`make info' here to do that automatically)."
|
||||
|
||||
info:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo "Running Texinfo files through makeinfo..."
|
||||
make -C $(BUILDDIR)/texinfo info
|
||||
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
||||
|
||||
gettext:
|
||||
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
||||
@echo
|
||||
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
||||
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 3.9 KiB |
|
@ -0,0 +1,16 @@
|
|||
<p class="logo"><a href="http://doc.openerp.com/">
|
||||
<img class="logo" src="{{ pathto('_static/openerp.png', 1) }}" alt="Logo"/>
|
||||
</a></p>
|
||||
|
||||
<h3>Other Docs</h3>
|
||||
<ul>
|
||||
<li><a href="http://doc.openerp.com/trunk/developers">OpenERP Developers Documentation</a></li>
|
||||
<li><a href="http://doc.openerp.com/trunk/developers/web">OpenERP Web Developers Documentation</a></li>
|
||||
<li><a href="http://doc.openerp.com/trunk/users">OpenERP Users Documentation</a></li>
|
||||
</ul>
|
||||
|
||||
<h3>Useful Links</h3>
|
||||
<ul>
|
||||
<li><a href="http://www.openerp.com/">The OpenERP website</a></li>
|
||||
<li><a href="http://python.org/">The Python programming language</a></li>
|
||||
</ul>
|
|
@ -0,0 +1,3 @@
|
|||
<p class="logo"><a href="{{ pathto(master_doc) }}">
|
||||
<img class="logo" src="{{ pathto('_static/openerp.png', 1) }}" alt="Logo"/>
|
||||
</a></p>
|
|
@ -0,0 +1,3 @@
|
|||
*.pyc
|
||||
*.pyo
|
||||
.DS_Store
|
|
@ -0,0 +1,37 @@
|
|||
Copyright (c) 2010 by Armin Ronacher.
|
||||
|
||||
Some rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms of the theme, with or
|
||||
without modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
* The names of the contributors may not be used to endorse or
|
||||
promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
We kindly ask you to only use these themes in an unmodified manner just
|
||||
for Flask and Flask-related products, not for unrelated projects. If you
|
||||
like the visual style and want to use it for your own projects, please
|
||||
consider making some larger changes to the themes (such as changing
|
||||
font faces, sizes, colors or margins).
|
||||
|
||||
THIS THEME IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS THEME, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,31 @@
|
|||
Flask Sphinx Styles
|
||||
===================
|
||||
|
||||
This repository contains sphinx styles for Flask and Flask related
|
||||
projects. To use this style in your Sphinx documentation, follow
|
||||
this guide:
|
||||
|
||||
1. put this folder as _themes into your docs folder. Alternatively
|
||||
you can also use git submodules to check out the contents there.
|
||||
2. add this to your conf.py:
|
||||
|
||||
sys.path.append(os.path.abspath('_themes'))
|
||||
html_theme_path = ['_themes']
|
||||
html_theme = 'flask'
|
||||
|
||||
The following themes exist:
|
||||
|
||||
- 'flask' - the standard flask documentation theme for large
|
||||
projects
|
||||
- 'flask_small' - small one-page theme. Intended to be used by
|
||||
very small addon libraries for flask.
|
||||
|
||||
The following options exist for the flask_small theme:
|
||||
|
||||
[options]
|
||||
index_logo = '' filename of a picture in _static
|
||||
to be used as replacement for the
|
||||
h1 in the index.rst file.
|
||||
index_logo_height = 120px height of the index logo
|
||||
github_fork = '' repository name on github for the
|
||||
"fork me" badge
|
|
@ -0,0 +1,25 @@
|
|||
{%- extends "basic/layout.html" %}
|
||||
{%- block extrahead %}
|
||||
{{ super() }}
|
||||
{% if theme_touch_icon %}
|
||||
<link rel="apple-touch-icon" href="{{ pathto('_static/' ~ theme_touch_icon, 1) }}" />
|
||||
{% endif %}
|
||||
<link media="only screen and (max-device-width: 480px)" href="{{
|
||||
pathto('_static/small_flask.css', 1) }}" type= "text/css" rel="stylesheet" />
|
||||
{% endblock %}
|
||||
{%- block relbar2 %}{% endblock %}
|
||||
{% block header %}
|
||||
{{ super() }}
|
||||
{% if pagename == 'index' %}
|
||||
<div class=indexwrapper>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{%- block footer %}
|
||||
<div class="footer">
|
||||
© Copyright {{ copyright }}
|
||||
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> and a modified <a href="https://github.com/mitsuhiko/flask-sphinx-themes">Flask theme</a>.
|
||||
</div>
|
||||
{% if pagename == 'index' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{%- endblock %}
|
|
@ -0,0 +1,19 @@
|
|||
<h3>Related Topics</h3>
|
||||
<ul>
|
||||
<li><a href="{{ pathto(master_doc) }}">Documentation overview</a><ul>
|
||||
{%- for parent in parents %}
|
||||
<li><a href="{{ parent.link|e }}">{{ parent.title }}</a><ul>
|
||||
{%- endfor %}
|
||||
{%- if prev %}
|
||||
<li>Previous: <a href="{{ prev.link|e }}" title="{{ _('previous chapter')
|
||||
}}">{{ prev.title }}</a></li>
|
||||
{%- endif %}
|
||||
{%- if next %}
|
||||
<li>Next: <a href="{{ next.link|e }}" title="{{ _('next chapter')
|
||||
}}">{{ next.title }}</a></li>
|
||||
{%- endif %}
|
||||
{%- for parent in parents %}
|
||||
</ul></li>
|
||||
{%- endfor %}
|
||||
</ul></li>
|
||||
</ul>
|
|
@ -0,0 +1,395 @@
|
|||
/*
|
||||
* flasky.css_t
|
||||
* ~~~~~~~~~~~~
|
||||
*
|
||||
* :copyright: Copyright 2010 by Armin Ronacher.
|
||||
* :license: Flask Design License, see LICENSE for details.
|
||||
*/
|
||||
|
||||
{% set page_width = '80em' %}
|
||||
{% set sidebar_width = '16em' %}
|
||||
|
||||
@import url("basic.css");
|
||||
|
||||
/* -- page layout ----------------------------------------------------------- */
|
||||
|
||||
body {
|
||||
font-family: 'Georgia', serif;
|
||||
font-size: 15px;
|
||||
background-color: white;
|
||||
color: #000;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.document {
|
||||
width: {{ page_width }};
|
||||
margin: 30px auto 0 auto;
|
||||
}
|
||||
|
||||
div.documentwrapper {
|
||||
float: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.bodywrapper {
|
||||
margin: 0 0 0 {{ sidebar_width }};
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
width: {{ sidebar_width }};
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 1px solid #B1B4B6;
|
||||
}
|
||||
|
||||
div.body {
|
||||
background-color: #ffffff;
|
||||
color: #3E4349;
|
||||
padding: 0 0px 0 0px;
|
||||
}
|
||||
|
||||
img.floatingflask {
|
||||
padding: 0 0 10px 10px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
div.footer {
|
||||
width: {{ page_width }};
|
||||
margin: 20px auto 30px auto;
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
div.footer a {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
div.related {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.sphinxsidebar a {
|
||||
color: #444;
|
||||
text-decoration: none;
|
||||
border-bottom: 1px dotted #999;
|
||||
}
|
||||
|
||||
div.sphinxsidebar a:hover {
|
||||
border-bottom: 1px solid #999;
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
div.sphinxsidebarwrapper {
|
||||
padding: 0px 10px;
|
||||
}
|
||||
|
||||
div.sphinxsidebarwrapper p.logo {
|
||||
padding: 0 0 20px 0;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3,
|
||||
div.sphinxsidebar h4 {
|
||||
font-family: 'Garamond', 'Georgia', serif;
|
||||
color: #444;
|
||||
font-size: 22px;
|
||||
font-weight: normal;
|
||||
margin: 0 0 5px 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h4 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3 a {
|
||||
color: #444;
|
||||
}
|
||||
|
||||
div.sphinxsidebar p.logo a,
|
||||
div.sphinxsidebar h3 a,
|
||||
div.sphinxsidebar p.logo a:hover,
|
||||
div.sphinxsidebar h3 a:hover {
|
||||
border: none;
|
||||
}
|
||||
|
||||
div.sphinxsidebar p {
|
||||
color: #555;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul {
|
||||
margin: 10px 0;
|
||||
padding: 0;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
div.sphinxsidebar input {
|
||||
border: 1px solid #ccc;
|
||||
font-family: 'Georgia', serif;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
/* -- body styles ----------------------------------------------------------- */
|
||||
|
||||
a {
|
||||
color: #004B6B;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #6D4100;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
div.body h1,
|
||||
div.body h2,
|
||||
div.body h3,
|
||||
div.body h4,
|
||||
div.body h5,
|
||||
div.body h6 {
|
||||
font-family: 'Garamond', 'Georgia', serif;
|
||||
font-weight: normal;
|
||||
margin: 30px 0px 10px 0px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
{% if theme_index_logo %}
|
||||
div.indexwrapper h1 {
|
||||
text-indent: -999999px;
|
||||
background: url({{ theme_index_logo }}) no-repeat center center;
|
||||
height: {{ theme_index_logo_height }};
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }
|
||||
div.body h2 { font-size: 180%; }
|
||||
div.body h3 { font-size: 150%; }
|
||||
div.body h4 { font-size: 130%; }
|
||||
div.body h5 { font-size: 100%; }
|
||||
div.body h6 { font-size: 100%; }
|
||||
|
||||
a.headerlink {
|
||||
color: #ddd;
|
||||
padding: 0 4px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a.headerlink:hover {
|
||||
color: #444;
|
||||
background: #eaeaea;
|
||||
}
|
||||
|
||||
div.body p, div.body dd, div.body li {
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
div.admonition {
|
||||
background: #fafafa;
|
||||
margin: 20px -30px;
|
||||
padding: 10px 30px;
|
||||
border-top: 1px solid #ccc;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
div.admonition tt.xref, div.admonition a tt {
|
||||
border-bottom: 1px solid #fafafa;
|
||||
}
|
||||
|
||||
dd div.admonition {
|
||||
margin-left: -60px;
|
||||
padding-left: 60px;
|
||||
}
|
||||
|
||||
div.admonition p.admonition-title {
|
||||
font-family: 'Garamond', 'Georgia', serif;
|
||||
font-weight: normal;
|
||||
font-size: 22px;
|
||||
margin: 0 0 10px 0;
|
||||
padding: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
div.admonition p.last {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div.highlight {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
dt:target, .highlight {
|
||||
background: #FAF3E8;
|
||||
}
|
||||
|
||||
div.note {
|
||||
background-color: #eee;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
div.seealso {
|
||||
background-color: #ffc;
|
||||
border: 1px solid #ff6;
|
||||
}
|
||||
|
||||
div.topic {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
p.admonition-title {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
p.admonition-title:after {
|
||||
content: ":";
|
||||
}
|
||||
|
||||
pre, tt {
|
||||
font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
img.screenshot {
|
||||
}
|
||||
|
||||
tt.descname, tt.descclassname {
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
tt.descname {
|
||||
padding-right: 0.08em;
|
||||
}
|
||||
|
||||
img.screenshot {
|
||||
-moz-box-shadow: 2px 2px 4px #eee;
|
||||
-webkit-box-shadow: 2px 2px 4px #eee;
|
||||
box-shadow: 2px 2px 4px #eee;
|
||||
}
|
||||
|
||||
table.docutils {
|
||||
border: 1px solid #888;
|
||||
-moz-box-shadow: 2px 2px 4px #eee;
|
||||
-webkit-box-shadow: 2px 2px 4px #eee;
|
||||
box-shadow: 2px 2px 4px #eee;
|
||||
}
|
||||
|
||||
table.docutils td, table.docutils th {
|
||||
border: 1px solid #888;
|
||||
padding: 0.25em 0.7em;
|
||||
}
|
||||
|
||||
table.field-list, table.footnote {
|
||||
border: none;
|
||||
-moz-box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
table.footnote {
|
||||
margin: 15px 0;
|
||||
width: 100%;
|
||||
border: 1px solid #eee;
|
||||
background: #fdfdfd;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
table.footnote + table.footnote {
|
||||
margin-top: -15px;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
table.field-list th {
|
||||
padding: 0 0.8em 0 0;
|
||||
}
|
||||
|
||||
table.field-list td {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
table.footnote td.label {
|
||||
width: 0px;
|
||||
padding: 0.3em 0 0.3em 0.5em;
|
||||
}
|
||||
|
||||
table.footnote td {
|
||||
padding: 0.3em 0.5em;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
dl dd {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 0 30px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
margin: 10px 0 10px 30px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
pre {
|
||||
background: #eee;
|
||||
padding: 7px 30px;
|
||||
margin: 15px -30px;
|
||||
line-height: 1.3em;
|
||||
}
|
||||
|
||||
dl pre, blockquote pre, li pre {
|
||||
margin-left: -60px;
|
||||
padding-left: 60px;
|
||||
}
|
||||
|
||||
dl dl pre {
|
||||
margin-left: -90px;
|
||||
padding-left: 90px;
|
||||
}
|
||||
|
||||
tt {
|
||||
background-color: #ecf0f3;
|
||||
color: #222;
|
||||
/* padding: 1px 2px; */
|
||||
}
|
||||
|
||||
tt.xref, a tt {
|
||||
background-color: #FBFBFB;
|
||||
border-bottom: 1px solid white;
|
||||
}
|
||||
|
||||
a.reference {
|
||||
text-decoration: none;
|
||||
border-bottom: 1px dotted #004B6B;
|
||||
}
|
||||
|
||||
a.reference:hover {
|
||||
border-bottom: 1px solid #6D4100;
|
||||
}
|
||||
|
||||
a.footnote-reference {
|
||||
text-decoration: none;
|
||||
font-size: 0.7em;
|
||||
vertical-align: top;
|
||||
border-bottom: 1px dotted #004B6B;
|
||||
}
|
||||
|
||||
a.footnote-reference:hover {
|
||||
border-bottom: 1px solid #6D4100;
|
||||
}
|
||||
|
||||
a:hover tt {
|
||||
background: #EEE;
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* small_flask.css_t
|
||||
* ~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* :copyright: Copyright 2010 by Armin Ronacher.
|
||||
* :license: Flask Design License, see LICENSE for details.
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 20px 30px;
|
||||
}
|
||||
|
||||
div.documentwrapper {
|
||||
float: none;
|
||||
background: white;
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
display: block;
|
||||
float: none;
|
||||
width: 102.5%;
|
||||
margin: 50px -30px -20px -30px;
|
||||
padding: 10px 20px;
|
||||
background: #333;
|
||||
color: white;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p,
|
||||
div.sphinxsidebar h3 a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
div.sphinxsidebar a {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
div.sphinxsidebar p.logo {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.document {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.related {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 10px 0 20px 0;
|
||||
}
|
||||
|
||||
div.related ul,
|
||||
div.related ul li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.bodywrapper {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.body {
|
||||
min-height: 0;
|
||||
padding: 0;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
[theme]
|
||||
inherit = basic
|
||||
stylesheet = flasky.css
|
||||
pygments_style = flask_theme_support.FlaskyStyle
|
||||
|
||||
[options]
|
||||
index_logo = ''
|
||||
index_logo_height = 120px
|
||||
touch_icon =
|
|
@ -0,0 +1,22 @@
|
|||
{% extends "basic/layout.html" %}
|
||||
{% block header %}
|
||||
{{ super() }}
|
||||
{% if pagename == 'index' %}
|
||||
<div class=indexwrapper>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% block footer %}
|
||||
{% if pagename == 'index' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{# do not display relbars #}
|
||||
{% block relbar1 %}{% endblock %}
|
||||
{% block relbar2 %}
|
||||
{% if theme_github_fork %}
|
||||
<a href="http://github.com/{{ theme_github_fork }}"><img style="position: fixed; top: 0; right: 0; border: 0;"
|
||||
src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" /></a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% block sidebar1 %}{% endblock %}
|
||||
{% block sidebar2 %}{% endblock %}
|
|
@ -0,0 +1,287 @@
|
|||
/*
|
||||
* flasky.css_t
|
||||
* ~~~~~~~~~~~~
|
||||
*
|
||||
* Sphinx stylesheet -- flasky theme based on nature theme.
|
||||
*
|
||||
* :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
||||
@import url("basic.css");
|
||||
|
||||
/* -- page layout ----------------------------------------------------------- */
|
||||
|
||||
body {
|
||||
font-family: 'Georgia', serif;
|
||||
font-size: 17px;
|
||||
color: #000;
|
||||
background: white;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.documentwrapper {
|
||||
float: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.bodywrapper {
|
||||
margin: 40px auto 0 auto;
|
||||
width: 700px;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 1px solid #B1B4B6;
|
||||
}
|
||||
|
||||
div.body {
|
||||
background-color: #ffffff;
|
||||
color: #3E4349;
|
||||
padding: 0 30px 30px 30px;
|
||||
}
|
||||
|
||||
img.floatingflask {
|
||||
padding: 0 0 10px 10px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
div.footer {
|
||||
text-align: right;
|
||||
color: #888;
|
||||
padding: 10px;
|
||||
font-size: 14px;
|
||||
width: 650px;
|
||||
margin: 0 auto 40px auto;
|
||||
}
|
||||
|
||||
div.footer a {
|
||||
color: #888;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
div.related {
|
||||
line-height: 32px;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
div.related ul {
|
||||
padding: 0 0 0 10px;
|
||||
}
|
||||
|
||||
div.related a {
|
||||
color: #444;
|
||||
}
|
||||
|
||||
/* -- body styles ----------------------------------------------------------- */
|
||||
|
||||
a {
|
||||
color: #004B6B;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #6D4100;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
div.body {
|
||||
padding-bottom: 40px; /* saved for footer */
|
||||
}
|
||||
|
||||
div.body h1,
|
||||
div.body h2,
|
||||
div.body h3,
|
||||
div.body h4,
|
||||
div.body h5,
|
||||
div.body h6 {
|
||||
font-family: 'Garamond', 'Georgia', serif;
|
||||
font-weight: normal;
|
||||
margin: 30px 0px 10px 0px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
{% if theme_index_logo %}
|
||||
div.indexwrapper h1 {
|
||||
text-indent: -999999px;
|
||||
background: url({{ theme_index_logo }}) no-repeat center center;
|
||||
height: {{ theme_index_logo_height }};
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
div.body h2 { font-size: 180%; }
|
||||
div.body h3 { font-size: 150%; }
|
||||
div.body h4 { font-size: 130%; }
|
||||
div.body h5 { font-size: 100%; }
|
||||
div.body h6 { font-size: 100%; }
|
||||
|
||||
a.headerlink {
|
||||
color: white;
|
||||
padding: 0 4px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a.headerlink:hover {
|
||||
color: #444;
|
||||
background: #eaeaea;
|
||||
}
|
||||
|
||||
div.body p, div.body dd, div.body li {
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
div.admonition {
|
||||
background: #fafafa;
|
||||
margin: 20px -30px;
|
||||
padding: 10px 30px;
|
||||
border-top: 1px solid #ccc;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
div.admonition p.admonition-title {
|
||||
font-family: 'Garamond', 'Georgia', serif;
|
||||
font-weight: normal;
|
||||
font-size: 24px;
|
||||
margin: 0 0 10px 0;
|
||||
padding: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
div.admonition p.last {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div.highlight{
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
dt:target, .highlight {
|
||||
background: #FAF3E8;
|
||||
}
|
||||
|
||||
div.note {
|
||||
background-color: #eee;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
div.seealso {
|
||||
background-color: #ffc;
|
||||
border: 1px solid #ff6;
|
||||
}
|
||||
|
||||
div.topic {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
div.warning {
|
||||
background-color: #ffe4e4;
|
||||
border: 1px solid #f66;
|
||||
}
|
||||
|
||||
p.admonition-title {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
p.admonition-title:after {
|
||||
content: ":";
|
||||
}
|
||||
|
||||
pre, tt {
|
||||
font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
img.screenshot {
|
||||
}
|
||||
|
||||
tt.descname, tt.descclassname {
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
tt.descname {
|
||||
padding-right: 0.08em;
|
||||
}
|
||||
|
||||
img.screenshot {
|
||||
-moz-box-shadow: 2px 2px 4px #eee;
|
||||
-webkit-box-shadow: 2px 2px 4px #eee;
|
||||
box-shadow: 2px 2px 4px #eee;
|
||||
}
|
||||
|
||||
table.docutils {
|
||||
border: 1px solid #888;
|
||||
-moz-box-shadow: 2px 2px 4px #eee;
|
||||
-webkit-box-shadow: 2px 2px 4px #eee;
|
||||
box-shadow: 2px 2px 4px #eee;
|
||||
}
|
||||
|
||||
table.docutils td, table.docutils th {
|
||||
border: 1px solid #888;
|
||||
padding: 0.25em 0.7em;
|
||||
}
|
||||
|
||||
table.field-list, table.footnote {
|
||||
border: none;
|
||||
-moz-box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
table.footnote {
|
||||
margin: 15px 0;
|
||||
width: 100%;
|
||||
border: 1px solid #eee;
|
||||
}
|
||||
|
||||
table.field-list th {
|
||||
padding: 0 0.8em 0 0;
|
||||
}
|
||||
|
||||
table.field-list td {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
table.footnote td {
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
dl dd {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 0;
|
||||
margin: 15px -30px;
|
||||
padding: 8px;
|
||||
line-height: 1.3em;
|
||||
padding: 7px 30px;
|
||||
background: #eee;
|
||||
border-radius: 2px;
|
||||
-moz-border-radius: 2px;
|
||||
-webkit-border-radius: 2px;
|
||||
}
|
||||
|
||||
dl pre {
|
||||
margin-left: -60px;
|
||||
padding-left: 60px;
|
||||
}
|
||||
|
||||
tt {
|
||||
background-color: #ecf0f3;
|
||||
color: #222;
|
||||
/* padding: 1px 2px; */
|
||||
}
|
||||
|
||||
tt.xref, a tt {
|
||||
background-color: #FBFBFB;
|
||||
}
|
||||
|
||||
a:hover tt {
|
||||
background: #EEE;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
[theme]
|
||||
inherit = basic
|
||||
stylesheet = flasky.css
|
||||
nosidebar = true
|
||||
pygments_style = flask_theme_support.FlaskyStyle
|
||||
|
||||
[options]
|
||||
index_logo = ''
|
||||
index_logo_height = 120px
|
||||
github_fork = ''
|
|
@ -0,0 +1,86 @@
|
|||
# flasky extensions. flasky pygments style based on tango style
|
||||
from pygments.style import Style
|
||||
from pygments.token import Keyword, Name, Comment, String, Error, \
|
||||
Number, Operator, Generic, Whitespace, Punctuation, Other, Literal
|
||||
|
||||
|
||||
class FlaskyStyle(Style):
|
||||
background_color = "#f8f8f8"
|
||||
default_style = ""
|
||||
|
||||
styles = {
|
||||
# No corresponding class for the following:
|
||||
#Text: "", # class: ''
|
||||
Whitespace: "underline #f8f8f8", # class: 'w'
|
||||
Error: "#a40000 border:#ef2929", # class: 'err'
|
||||
Other: "#000000", # class 'x'
|
||||
|
||||
Comment: "italic #8f5902", # class: 'c'
|
||||
Comment.Preproc: "noitalic", # class: 'cp'
|
||||
|
||||
Keyword: "bold #004461", # class: 'k'
|
||||
Keyword.Constant: "bold #004461", # class: 'kc'
|
||||
Keyword.Declaration: "bold #004461", # class: 'kd'
|
||||
Keyword.Namespace: "bold #004461", # class: 'kn'
|
||||
Keyword.Pseudo: "bold #004461", # class: 'kp'
|
||||
Keyword.Reserved: "bold #004461", # class: 'kr'
|
||||
Keyword.Type: "bold #004461", # class: 'kt'
|
||||
|
||||
Operator: "#582800", # class: 'o'
|
||||
Operator.Word: "bold #004461", # class: 'ow' - like keywords
|
||||
|
||||
Punctuation: "bold #000000", # class: 'p'
|
||||
|
||||
# because special names such as Name.Class, Name.Function, etc.
|
||||
# are not recognized as such later in the parsing, we choose them
|
||||
# to look the same as ordinary variables.
|
||||
Name: "#000000", # class: 'n'
|
||||
Name.Attribute: "#c4a000", # class: 'na' - to be revised
|
||||
Name.Builtin: "#004461", # class: 'nb'
|
||||
Name.Builtin.Pseudo: "#3465a4", # class: 'bp'
|
||||
Name.Class: "#000000", # class: 'nc' - to be revised
|
||||
Name.Constant: "#000000", # class: 'no' - to be revised
|
||||
Name.Decorator: "#888", # class: 'nd' - to be revised
|
||||
Name.Entity: "#ce5c00", # class: 'ni'
|
||||
Name.Exception: "bold #cc0000", # class: 'ne'
|
||||
Name.Function: "#000000", # class: 'nf'
|
||||
Name.Property: "#000000", # class: 'py'
|
||||
Name.Label: "#f57900", # class: 'nl'
|
||||
Name.Namespace: "#000000", # class: 'nn' - to be revised
|
||||
Name.Other: "#000000", # class: 'nx'
|
||||
Name.Tag: "bold #004461", # class: 'nt' - like a keyword
|
||||
Name.Variable: "#000000", # class: 'nv' - to be revised
|
||||
Name.Variable.Class: "#000000", # class: 'vc' - to be revised
|
||||
Name.Variable.Global: "#000000", # class: 'vg' - to be revised
|
||||
Name.Variable.Instance: "#000000", # class: 'vi' - to be revised
|
||||
|
||||
Number: "#990000", # class: 'm'
|
||||
|
||||
Literal: "#000000", # class: 'l'
|
||||
Literal.Date: "#000000", # class: 'ld'
|
||||
|
||||
String: "#4e9a06", # class: 's'
|
||||
String.Backtick: "#4e9a06", # class: 'sb'
|
||||
String.Char: "#4e9a06", # class: 'sc'
|
||||
String.Doc: "italic #8f5902", # class: 'sd' - like a comment
|
||||
String.Double: "#4e9a06", # class: 's2'
|
||||
String.Escape: "#4e9a06", # class: 'se'
|
||||
String.Heredoc: "#4e9a06", # class: 'sh'
|
||||
String.Interpol: "#4e9a06", # class: 'si'
|
||||
String.Other: "#4e9a06", # class: 'sx'
|
||||
String.Regex: "#4e9a06", # class: 'sr'
|
||||
String.Single: "#4e9a06", # class: 's1'
|
||||
String.Symbol: "#4e9a06", # class: 'ss'
|
||||
|
||||
Generic: "#000000", # class: 'g'
|
||||
Generic.Deleted: "#a40000", # class: 'gd'
|
||||
Generic.Emph: "italic #000000", # class: 'ge'
|
||||
Generic.Error: "#ef2929", # class: 'gr'
|
||||
Generic.Heading: "bold #000080", # class: 'gh'
|
||||
Generic.Inserted: "#00A000", # class: 'gi'
|
||||
Generic.Output: "#888", # class: 'go'
|
||||
Generic.Prompt: "#745334", # class: 'gp'
|
||||
Generic.Strong: "bold #000000", # class: 'gs'
|
||||
Generic.Subheading: "bold #800080", # class: 'gu'
|
||||
Generic.Traceback: "bold #a40000", # class: 'gt'
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
.. _openerp library:
|
||||
|
||||
Server-side library
|
||||
-------------------
|
||||
|
||||
.. automodule:: openerp
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
ORM and models
|
||||
--------------
|
||||
|
||||
.. automodule:: openerp.osv.orm
|
||||
:members:
|
||||
:undoc-members:
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
Start-up script
|
||||
---------------
|
||||
|
||||
To run the OpenERP server, the conventional approach is to use the
|
||||
`openerp-server` script. It loads the :ref:`openerp library`, sets a few
|
||||
configuration variables corresponding to command-line arguments, and starts to
|
||||
listen to incoming connections from clients.
|
||||
|
||||
Depending on your deployment needs, you can write such a start-up script very
|
||||
easily. We also recommend you take a look at an alternative tool called
|
||||
`openerp-command` that can, among other things, launch the server.
|
||||
|
||||
.. versionadded:: 6.1
|
||||
|
||||
Yet another alternative is to use a WSGI-compatible HTTP server and let it call
|
||||
into one of the WSGI entry points of the server.
|
||||
|
||||
.. versionadded:: 6.1
|
||||
|
|
@ -0,0 +1,256 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# OpenERP Technical Documentation configuration file, created by
|
||||
# sphinx-quickstart on Fri Feb 17 16:14:06 2012.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys, os
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.insert(0, os.path.abspath('.'))
|
||||
sys.path.append(os.path.abspath('_themes'))
|
||||
sys.path.append(os.path.abspath('..'))
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.viewcode']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'OpenERP Server Developers Documentation'
|
||||
copyright = u'2012, OpenERP s.a.'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '6.1'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '6.1'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = ['_build']
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'flask'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
html_theme_path = ['_themes']
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
html_sidebars = {
|
||||
'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'],
|
||||
'**': ['sidebarlogo.html', 'localtoc.html', 'relations.html',
|
||||
'sourcelink.html', 'searchbox.html']
|
||||
}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'openerp-server-doc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#'preamble': '',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'openerp-server-doc.tex', u'OpenERP Server Developers Documentation',
|
||||
u'OpenERP s.a.', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output --------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'openerp-server-doc', u'OpenERP Server Developers Documentation',
|
||||
[u'OpenERP s.a.'], 1)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output ------------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'OpenERPServerDocumentation', u'OpenERP Server Developers Documentation',
|
||||
u'OpenERP s.a.', 'OpenERPServerDocumentation', 'Developers documentation for the openobject-server project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
#texinfo_show_urls = 'footnote'
|
||||
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
intersphinx_mapping = {
|
||||
'python': ('http://docs.python.org/', None),
|
||||
'openerpweb': ('http://doc.openerp.com/trunk/developers/web', None),
|
||||
'openerpdev': ('http://doc.openerp.com/trunk/developers', None),
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
:orphan:
|
||||
|
||||
OpenERP Server Developers Documentation
|
||||
=======================================
|
||||
|
||||
.. include:: index.rst.inc
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
OpenERP Server
|
||||
''''''''''''''
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
test-framework
|
|
@ -0,0 +1,100 @@
|
|||
.. _test-framework:
|
||||
|
||||
Test framework
|
||||
==============
|
||||
|
||||
In addition to the YAML-based tests, OpenERP uses the unittest2_ testing
|
||||
framework to test both the core ``openerp`` package and its addons. For the
|
||||
core and each addons, tests are divided between three (overlapping) sets:
|
||||
|
||||
1. A test suite that comprises all the tests that can be run right after the
|
||||
addons is installed (or, for the core, right after a database is created).
|
||||
That suite is called ``fast_suite`` and must contain only tests that can be run
|
||||
frequently. Actually most of the tests should be considered fast enough to be
|
||||
included in that ``fast_suite`` list and only tests that take a long time to run
|
||||
(e.g. more than a minute) should not be listed. Those long tests should come up
|
||||
pretty rarely.
|
||||
|
||||
2. A test suite called ``checks`` provides sanity checks. These tests are
|
||||
invariants that must be full-filled at any time. They are expected to always
|
||||
pass: obviously they must pass right after the module is installed (i.e. just
|
||||
like the ``fast_suite`` tests), but they must also pass after any other module is
|
||||
installed, after a migration, or even after the database was put in production
|
||||
for a few months.
|
||||
|
||||
3. The third suite is made of all the tests: those provided by the two above
|
||||
suites, but also tests that are not explicitely listed in ``fast_suite`` or
|
||||
``checks``. They are not explicitely listed anywhere and are discovered
|
||||
automatically.
|
||||
|
||||
As the sanity checks provide stronger guarantees about the code and database
|
||||
structure, new tests must be added to the ``checks`` suite whenever it is
|
||||
possible. Said with other words: one should try to avoid writing tests that
|
||||
assume a freshly installed/unaltered module or database.
|
||||
|
||||
It is possible to have tests that are not listed in ``fast_suite`` or
|
||||
``checks``. This is useful if a test takes a lot of time. By default, when
|
||||
using the testing infrastructure, tests should run fast enough so that people
|
||||
can use them frequently. One can also use that possiblity for tests that
|
||||
require some complex setup before they can be successfuly run.
|
||||
|
||||
As a rule of thumb when writing a new test, try to add it to the ``checks``
|
||||
suite. If it really needs that the module it belongs to is freshly installed,
|
||||
add it to ``fast_suite``. Finally, if it can not be run in an acceptable time
|
||||
frame, don't add it to any explicit list.
|
||||
|
||||
Writing tests
|
||||
-------------
|
||||
|
||||
The tests must be developed under ``<addons-name>.tests`` (or ``openerp.tests``
|
||||
for the core). For instance, with respect to the tests, a module ``foo``
|
||||
should be organized as follow::
|
||||
|
||||
foo/
|
||||
__init__.py # does not import .tests
|
||||
tests/
|
||||
__init__.py # import some of the tests sub-modules, and
|
||||
# list them in fast_suite or checks
|
||||
test_bar.py # contains unittest2 classes
|
||||
test_baz.py # idem
|
||||
... and so on ...
|
||||
|
||||
The two explicit lists of tests are thus the variables ``foo.tests.fast_suite``
|
||||
and ``foo.tests.checks``. As an example, you can take a look at the
|
||||
``openerp.tests`` module (which follows exactly the same conventions even if it
|
||||
is not an addons).
|
||||
|
||||
Note that the ``fast_suite`` and ``checks`` variables are really lists of
|
||||
module objects. They could be directly unittest2 suite objects if necessary in
|
||||
the future.
|
||||
|
||||
Running the tests
|
||||
-----------------
|
||||
|
||||
To run the tests (see :ref:`above <test-framework>` to learn how tests are
|
||||
organized), the simplest way is to use the ``oe`` command (provided by the
|
||||
``openerp-command`` project).
|
||||
|
||||
::
|
||||
|
||||
> oe run-tests # will run all the fast_suite tests
|
||||
> oe run-tests -m openerp # will run all the fast_suite tests defined in `openerp.tests`
|
||||
> oe run-tests -m sale # will run all the fast_suite tests defined in `openerp.addons.sale.tests`
|
||||
> oe run-tests -m foo.test_bar # will run the tests defined in `openerp.addons.foo.tests.test_bar`
|
||||
|
||||
In addition to the above possibilities, when invoked with a non-existing module
|
||||
(or module.sub-module) name, oe will reply with a list of available test
|
||||
sub-modules.
|
||||
|
||||
Depending on the unittest2_ class that is used to write the tests (see
|
||||
``openerp.tests.common`` for some helper classes that you can re-use), a database
|
||||
may be created before the test is run, and the module providing the test will
|
||||
be installed on that database.
|
||||
|
||||
Because creating a database, installing modules, and then dropping it is
|
||||
expensive, it is possible to interleave the run of the ``fast_suite`` tests
|
||||
with the initialization of a new database: the dabase is created, and after
|
||||
each requested module is installed, its fast_suite tests are run. The database
|
||||
is thus created and dropped (and the modules installed) only once.
|
||||
|
||||
.. _unittest2: http://pypi.python.org/pypi/unittest2
|
|
@ -91,7 +91,6 @@
|
|||
'test/bug_lp541545.xml',
|
||||
'test/test_osv_expression.yml',
|
||||
'test/test_ir_rule.yml', # <-- These tests modify/add/delete ir_rules.
|
||||
'test/test_ir_values.yml',
|
||||
# Commented because this takes some time.
|
||||
# This must be (un)commented with the corresponding import statement
|
||||
# in test/__init__.py.
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -8,14 +8,14 @@ msgstr ""
|
|||
"Project-Id-Version: openobject-server\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-02-08 00:44+0000\n"
|
||||
"PO-Revision-Date: 2012-02-18 21:50+0000\n"
|
||||
"PO-Revision-Date: 2012-02-28 16:02+0000\n"
|
||||
"Last-Translator: Akira Hiyama <Unknown>\n"
|
||||
"Language-Team: Japanese <ja@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-19 05:41+0000\n"
|
||||
"X-Generator: Launchpad (build 14814)\n"
|
||||
"X-Launchpad-Export-Date: 2012-02-29 04:41+0000\n"
|
||||
"X-Generator: Launchpad (build 14874)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
@ -7930,73 +7930,73 @@ msgstr "スペイン語(メキシコ)"
|
|||
#: code:addons/base/publisher_warranty/publisher_warranty.py:145
|
||||
#, python-format
|
||||
msgid "Please verify your publisher warranty serial number and validity."
|
||||
msgstr ""
|
||||
msgstr "発行者の保証シリアル番号とその有効性を確認して下さい。"
|
||||
|
||||
#. module: base
|
||||
#: view:res.log:0
|
||||
msgid "My Logs"
|
||||
msgstr ""
|
||||
msgstr "マイログ"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.bt
|
||||
msgid "Bhutan"
|
||||
msgstr ""
|
||||
msgstr "ブータン"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.sequence,number_next:0
|
||||
msgid "Next number of this sequence"
|
||||
msgstr ""
|
||||
msgstr "この順番の次の番号"
|
||||
|
||||
#. module: base
|
||||
#: model:res.partner.category,name:base.res_partner_category_11
|
||||
msgid "Textile Suppliers"
|
||||
msgstr ""
|
||||
msgstr "繊維供給元"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.actions.url,target:0
|
||||
msgid "This Window"
|
||||
msgstr ""
|
||||
msgstr "このウィンドウ"
|
||||
|
||||
#. module: base
|
||||
#: view:publisher_warranty.contract:0
|
||||
msgid "Publisher Warranty Contracts"
|
||||
msgstr ""
|
||||
msgstr "発行者保証契約"
|
||||
|
||||
#. module: base
|
||||
#: help:res.log,name:0
|
||||
msgid "The logging message."
|
||||
msgstr ""
|
||||
msgstr "ロギングメッセージ"
|
||||
|
||||
#. module: base
|
||||
#: field:base.language.export,format:0
|
||||
msgid "File Format"
|
||||
msgstr ""
|
||||
msgstr "ファイル形式"
|
||||
|
||||
#. module: base
|
||||
#: field:res.lang,iso_code:0
|
||||
msgid "ISO code"
|
||||
msgstr ""
|
||||
msgstr "ISOコード"
|
||||
|
||||
#. module: base
|
||||
#: view:res.log:0
|
||||
#: field:res.log,read:0
|
||||
msgid "Read"
|
||||
msgstr ""
|
||||
msgstr "読み込み"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_association
|
||||
msgid "Associations Management"
|
||||
msgstr ""
|
||||
msgstr "アソシエーション管理"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.model,modules:0
|
||||
msgid "List of modules in which the object is defined or inherited"
|
||||
msgstr ""
|
||||
msgstr "オブジェクトが定義または継承されたモジュールのリスト"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_hr_payroll
|
||||
msgid "Payroll"
|
||||
msgstr ""
|
||||
msgstr "給与"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.actions.act_window,help:base.action_country_state
|
||||
|
@ -8004,7 +8004,7 @@ msgid ""
|
|||
"If you are working on the American market, you can manage the different "
|
||||
"federal states you are working on from here. Each state is attached to one "
|
||||
"country."
|
||||
msgstr ""
|
||||
msgstr "あなたがアメリカの市場で働いているなら、あなたはここからあなたが働く異なる州を管理できます。それぞれの州はひとつの国に属しています。"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_delivery
|
||||
|
@ -8019,23 +8019,31 @@ msgid ""
|
|||
"\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"受注と引き取りに関する配送方法を追加することができます。\n"
|
||||
"==============================================================\n"
|
||||
"\n"
|
||||
"価格によるあなた自身の運搬人や配送グリッドの定義ができます。\n"
|
||||
"引き取りから請求書を作成する場合は、OpenERPは配送の行を追加し計算を行います。\n"
|
||||
"\n"
|
||||
" "
|
||||
|
||||
#. module: base
|
||||
#: view:workflow.workitem:0
|
||||
msgid "Workflow Workitems"
|
||||
msgstr ""
|
||||
msgstr "ワークフロー作業項目"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.vc
|
||||
msgid "Saint Vincent & Grenadines"
|
||||
msgstr ""
|
||||
msgstr "セントビンセント・グレナディーン"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.mail_server,smtp_pass:0
|
||||
#: field:partner.sms.send,password:0
|
||||
#: field:res.users,password:0
|
||||
msgid "Password"
|
||||
msgstr ""
|
||||
msgstr "パスワード"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_anglo_saxon
|
||||
|
@ -8059,11 +8067,23 @@ msgid ""
|
|||
"Secondly, price differences between actual purchase price and fixed product "
|
||||
"standard price are booked on a separate account"
|
||||
msgstr ""
|
||||
"\n"
|
||||
"このモジュールは株式トランザクションの会計ロジックの変更によりアングロサクソン会計方式をサポートします。\n"
|
||||
"============================================================================="
|
||||
"========================================\n"
|
||||
"\n"
|
||||
"アングロサクソン会計(英米型会計)国と、ラインまたはコンチネンタル会計(大陸型会計)国と呼ばれる違いは\n"
|
||||
"売上原価の計上タイミングの違いです。\n"
|
||||
"アングロサクソン会計は販売の請求書が作成された時に原価を計上します。コンチネンタル会計では\n"
|
||||
"商品が出荷された時に原価を計上します。\n"
|
||||
"このモジュールは中間勘定を使うことによってこの機能を加えます。借方または貸方アカウントにその総額を転送\n"
|
||||
"するために請求書が作成された時に、出荷された商品の価値を保存し、そしてこの中間勘定を反対記帳します。\n"
|
||||
"第2に、実際の購入価格と固定の製品標準価格の差は分離された勘定に記帳されます。"
|
||||
|
||||
#. module: base
|
||||
#: field:res.partner,title:0
|
||||
msgid "Partner Firm"
|
||||
msgstr ""
|
||||
msgstr "パートナ企業"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.actions.act_window,name:base.action_model_fields
|
||||
|
@ -8073,12 +8093,12 @@ msgstr ""
|
|||
#: view:ir.model.fields:0
|
||||
#: model:ir.ui.menu,name:base.ir_model_model_fields
|
||||
msgid "Fields"
|
||||
msgstr ""
|
||||
msgstr "項目"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.actions.act_window,name:base.action_partner_employee_form
|
||||
msgid "Employees"
|
||||
msgstr ""
|
||||
msgstr "従業員"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.exports.line,name:0
|
||||
|
@ -8091,7 +8111,7 @@ msgstr "項目名"
|
|||
#: help:res.log,read:0
|
||||
msgid ""
|
||||
"If this log item has been read, get() should not send it to the client"
|
||||
msgstr ""
|
||||
msgstr "もしこのログ項目が読まれたなら、get()をクライアントに送るべきではありません。"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_web_uservoice
|
||||
|
@ -8103,48 +8123,54 @@ msgid ""
|
|||
"Invite OpenERP user feedback, powered by uservoice.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"ヘッダーの中にフィードバックボタンを加えて下さい。\n"
|
||||
"==============================\n"
|
||||
"\n"
|
||||
"ユーザの声によるOpenERPのユーザフィードバックを求めて下さい。\n"
|
||||
" "
|
||||
|
||||
#. module: base
|
||||
#: field:res.company,rml_header2:0
|
||||
#: field:res.company,rml_header3:0
|
||||
msgid "RML Internal Header"
|
||||
msgstr ""
|
||||
msgstr "RML内部ヘッダー"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.actions.act_window,search_view_id:0
|
||||
msgid "Search View Ref."
|
||||
msgstr ""
|
||||
msgstr "リファレンスの検索ビュー"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.module.module,installed_version:0
|
||||
msgid "Latest version"
|
||||
msgstr ""
|
||||
msgstr "最新バージョン"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.mail_server:0
|
||||
msgid "Test Connection"
|
||||
msgstr ""
|
||||
msgstr "接続テスト"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.actions.act_window,name:base.action_partner_address_form
|
||||
#: model:ir.ui.menu,name:base.menu_partner_address_form
|
||||
msgid "Addresses"
|
||||
msgstr ""
|
||||
msgstr "住所"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.mm
|
||||
msgid "Myanmar"
|
||||
msgstr ""
|
||||
msgstr "ミャンマー"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.model.fields,modules:0
|
||||
msgid "List of modules in which the field is defined"
|
||||
msgstr ""
|
||||
msgstr "項目が定義されているモジュールのリスト"
|
||||
|
||||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
msgid "Chinese (CN) / 简体中文"
|
||||
msgstr ""
|
||||
msgstr "中国語(简体中文)"
|
||||
|
||||
#. module: base
|
||||
#: field:res.bank,street:0
|
||||
|
@ -8157,7 +8183,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:res.country,name:base.yu
|
||||
msgid "Yugoslavia"
|
||||
msgstr ""
|
||||
msgstr "ユーゴスラビア"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_purchase_double_validation
|
||||
|
@ -8170,22 +8196,29 @@ msgid ""
|
|||
"that exceeds minimum amount set by configuration wizard.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"最低額を超える購入のための二重検証\n"
|
||||
"=========================================================\n"
|
||||
"\n"
|
||||
"このモジュールは、コンフィギュレーションウィザードによって設定される最低額を超えた購入を\n"
|
||||
"検証するための購入ワークフローを変更します。\n"
|
||||
" "
|
||||
|
||||
#. module: base
|
||||
#: field:res.currency,rounding:0
|
||||
msgid "Rounding Factor"
|
||||
msgstr ""
|
||||
msgstr "丸め係数"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.ca
|
||||
msgid "Canada"
|
||||
msgstr ""
|
||||
msgstr "カナダ"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/res/res_company.py:158
|
||||
#, python-format
|
||||
msgid "Reg: "
|
||||
msgstr ""
|
||||
msgstr "Reg: "
|
||||
|
||||
#. module: base
|
||||
#: help:res.currency.rate,currency_rate_type_id:0
|
||||
|
@ -8193,27 +8226,28 @@ msgid ""
|
|||
"Allow you to define your own currency rate types, like 'Average' or 'Year to "
|
||||
"Date'. Leave empty if you simply want to use the normal 'spot' rate type"
|
||||
msgstr ""
|
||||
"平均または年初から本日までといったあなた自身の通貨レートタイプを定義できます。通常のスポットレートを希望する場合には空白のままとして下さい。"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.module.module.dependency,state:0
|
||||
msgid "Unknown"
|
||||
msgstr ""
|
||||
msgstr "不明"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.actions.act_window,name:base.action_res_users_my
|
||||
msgid "Change My Preferences"
|
||||
msgstr ""
|
||||
msgstr "設定の変更"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/ir/ir_actions.py:167
|
||||
#, python-format
|
||||
msgid "Invalid model name in the action definition."
|
||||
msgstr ""
|
||||
msgstr "アクション定義におけるモデル名が無効です。"
|
||||
|
||||
#. module: base
|
||||
#: field:partner.sms.send,text:0
|
||||
msgid "SMS Message"
|
||||
msgstr ""
|
||||
msgstr "SMSメッセージ"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_l10n_ro
|
||||
|
@ -8227,21 +8261,28 @@ msgid ""
|
|||
"Romanian accounting chart and localization.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"このモジュールはルーマニアのOpenERPのため会計表、VAT構造、登録番号を管理するためのモジュールです。\n"
|
||||
"============================================================================="
|
||||
"===================================\n"
|
||||
"\n"
|
||||
"ルーマニアの会計表とローカル化\n"
|
||||
" "
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.cm
|
||||
msgid "Cameroon"
|
||||
msgstr ""
|
||||
msgstr "カメルーン"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.bf
|
||||
msgid "Burkina Faso"
|
||||
msgstr ""
|
||||
msgstr "ブルキナファソ"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.model.fields,state:0
|
||||
msgid "Custom Field"
|
||||
msgstr ""
|
||||
msgstr "カスタム項目"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_project_retro_planning
|
||||
|
@ -8254,6 +8295,12 @@ msgid ""
|
|||
"all the tasks will change accordingly.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"変更日はプロジェクトの終了日の変更に応じて変更されます。\n"
|
||||
"======================================================\n"
|
||||
"\n"
|
||||
"もしプロジェクトの終了日が変更された時は、すべてのタスクの期限と開始日が変更されます。\n"
|
||||
" "
|
||||
|
||||
#. module: base
|
||||
#: help:res.users,view:0
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,87 +0,0 @@
|
|||
-
|
||||
Create some default value for some (non-existing) model, for all users.
|
||||
-
|
||||
!python {model: ir.values }: |
|
||||
# use the old API
|
||||
self.set(cr, uid, 'default', False, 'my_test_field',['unexisting_model'], 'global value')
|
||||
# use the new API
|
||||
self.set_default(cr, uid, 'other_unexisting_model', 'my_other_test_field', 'conditional value', condition='foo=bar')
|
||||
-
|
||||
Retrieve them.
|
||||
-
|
||||
!python {model: ir.values }: |
|
||||
# d is a list of triplets (id, name, value)
|
||||
# Old API
|
||||
d = self.get(cr, uid, 'default', False, ['unexisting_model'])
|
||||
assert len(d) == 1, "Only one single value should be retrieved for this model"
|
||||
assert d[0][1] == 'my_test_field', "Can't retrieve the created default value. (1)"
|
||||
assert d[0][2] == 'global value', "Can't retrieve the created default value. (2)"
|
||||
|
||||
# New API, Conditional version
|
||||
d = self.get_defaults(cr, uid, 'other_unexisting_model')
|
||||
assert len(d) == 0, "No value should be retrieved, the condition is not met"
|
||||
d = self.get_defaults(cr, uid, 'other_unexisting_model', condition="foo=eggs")
|
||||
assert len(d) == 0, 'Condition is not met either, no defaults should be returned'
|
||||
d = self.get_defaults(cr, uid, 'other_unexisting_model', condition="foo=bar")
|
||||
assert len(d) == 1, "Only one single value should be retrieved"
|
||||
assert d[0][1] == 'my_other_test_field', "Can't retrieve the created default value. (5)"
|
||||
assert d[0][2] == 'conditional value', "Can't retrieve the created default value. (6)"
|
||||
-
|
||||
Do it again but for a specific user.
|
||||
-
|
||||
!python {model: ir.values }: |
|
||||
self.set(cr, uid, 'default', False, 'my_test_field',['unexisting_model'], 'specific value', preserve_user=True)
|
||||
-
|
||||
Retrieve it and check it is the one for the current user.
|
||||
-
|
||||
!python {model: ir.values }: |
|
||||
d = self.get(cr, uid, 'default', False, ['unexisting_model'])
|
||||
assert len(d) == 1, "Only one default must be returned per field"
|
||||
assert d[0][1] == 'my_test_field', "Can't retrieve the created default value."
|
||||
assert d[0][2] == 'specific value', "Can't retrieve the created default value."
|
||||
-
|
||||
Create some action bindings for a non-existing model
|
||||
-
|
||||
!python {model: ir.values }: |
|
||||
self.set(cr, uid, 'action', 'tree_but_open', 'OnDblClick Action', ['unexisting_model'], 'ir.actions.act_window,10', isobject=True)
|
||||
self.set(cr, uid, 'action', 'tree_but_open', 'OnDblClick Action 2', ['unexisting_model'], 'ir.actions.act_window,11', isobject=True)
|
||||
self.set(cr, uid, 'action', 'client_action_multi', 'Side Wizard', ['unexisting_model'], 'ir.actions.act_window,12', isobject=True)
|
||||
self.set(cr, uid, 'action', 'client_print_multi', 'Nice Report', ['unexisting_model'], 'ir.actions.report.xml,2', isobject=True)
|
||||
self.set(cr, uid, 'action', 'client_action_relate', 'Related Stuff', ['unexisting_model'], 'ir.actions.act_window,14', isobject=True)
|
||||
-
|
||||
Replace one action binding to set a new name
|
||||
-
|
||||
!python {model: ir.values }: |
|
||||
self.set(cr, uid, 'action', 'tree_but_open', 'OnDblClick Action New', ['unexisting_model'], 'ir.actions.act_window,10', isobject=True)
|
||||
-
|
||||
Retrieve the action bindings and check they're correct
|
||||
-
|
||||
!python {model: ir.values }: |
|
||||
actions = self.get(cr, uid, 'action', 'tree_but_open', ['unexisting_model'])
|
||||
assert len(actions) == 2, "Mismatching number of bound actions"
|
||||
#first action
|
||||
assert len(actions[0]) == 3, "Malformed action definition"
|
||||
assert actions[0][1] == 'OnDblClick Action 2', 'Bound action does not match definition'
|
||||
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 11, 'Bound action does not match definition'
|
||||
#second action - this ones comes last because it was re-created with a different name
|
||||
assert len(actions[1]) == 3, "Malformed action definition"
|
||||
assert actions[1][1] == 'OnDblClick Action New', 'Re-Registering an action should replace it'
|
||||
assert isinstance(actions[1][2], dict) and actions[1][2]['id'] == 10, 'Bound action does not match definition'
|
||||
|
||||
actions = self.get(cr, uid, 'action', 'client_action_multi', ['unexisting_model'])
|
||||
assert len(actions) == 1, "Mismatching number of bound actions"
|
||||
assert len(actions[0]) == 3, "Malformed action definition"
|
||||
assert actions[0][1] == 'Side Wizard', 'Bound action does not match definition'
|
||||
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 12, 'Bound action does not match definition'
|
||||
|
||||
actions = self.get(cr, uid, 'action', 'client_print_multi', ['unexisting_model'])
|
||||
assert len(actions) == 1, "Mismatching number of bound actions"
|
||||
assert len(actions[0]) == 3, "Malformed action definition"
|
||||
assert actions[0][1] == 'Nice Report', 'Bound action does not match definition'
|
||||
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 2, 'Bound action does not match definition'
|
||||
|
||||
actions = self.get(cr, uid, 'action', 'client_action_relate', ['unexisting_model'])
|
||||
assert len(actions) == 1, "Mismatching number of bound actions"
|
||||
assert len(actions[0]) == 3, "Malformed action definition"
|
||||
assert actions[0][1] == 'Related Stuff', 'Bound action does not match definition'
|
||||
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 14, 'Bound action does not match definition'
|
|
@ -440,16 +440,6 @@
|
|||
assert res_2 == expected
|
||||
assert res_3 == expected
|
||||
assert res_4 == expected
|
||||
-
|
||||
Verify that normalize_domain() works.
|
||||
-
|
||||
!python {model: res.partner}: |
|
||||
from osv import expression
|
||||
norm_domain = domain = ['&',(1,'=',1),('a','=','b')]
|
||||
assert norm_domain == expression.normalize(domain), "Normalized domains should be left untouched"
|
||||
domain = [('x','in',['y','z']),('a.v','=','e'),'|','|',('a','=','b'),'!',('c','>','d'),('e','!=','f'),('g','=','h')]
|
||||
norm_domain = ['&','&','&'] + domain
|
||||
assert norm_domain == expression.normalize(domain), "Non-normalized domains should be properly normalized"
|
||||
-
|
||||
Unaccent. Create a company with an accent in its name.
|
||||
-
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
import test_ir_values
|
||||
|
||||
checks = [
|
||||
test_ir_values,
|
||||
]
|
|
@ -0,0 +1,95 @@
|
|||
import unittest2
|
||||
|
||||
import openerp.tests.common as common
|
||||
|
||||
class test_ir_values(common.TransactionCase):
|
||||
|
||||
def test_00(self):
|
||||
# Create some default value for some (non-existing) model, for all users.
|
||||
|
||||
ir_values = self.registry('ir.values')
|
||||
# use the old API
|
||||
ir_values.set(self.cr, self.uid, 'default', False, 'my_test_field',
|
||||
['unexisting_model'], 'global value')
|
||||
# use the new API
|
||||
ir_values.set_default(self.cr, self.uid, 'other_unexisting_model',
|
||||
'my_other_test_field', 'conditional value', condition='foo=bar')
|
||||
|
||||
|
||||
# Retrieve them.
|
||||
|
||||
ir_values = self.registry('ir.values')
|
||||
# d is a list of triplets (id, name, value)
|
||||
# Old API
|
||||
d = ir_values.get(self.cr, self.uid, 'default', False, ['unexisting_model'])
|
||||
assert len(d) == 1, "Only one single value should be retrieved for this model"
|
||||
assert d[0][1] == 'my_test_field', "Can't retrieve the created default value. (1)"
|
||||
assert d[0][2] == 'global value', "Can't retrieve the created default value. (2)"
|
||||
|
||||
# New API, Conditional version
|
||||
d = ir_values.get_defaults(self.cr, self.uid, 'other_unexisting_model')
|
||||
assert len(d) == 0, "No value should be retrieved, the condition is not met"
|
||||
d = ir_values.get_defaults(self.cr, self.uid, 'other_unexisting_model', condition="foo=eggs")
|
||||
assert len(d) == 0, 'Condition is not met either, no defaults should be returned'
|
||||
d = ir_values.get_defaults(self.cr, self.uid, 'other_unexisting_model', condition="foo=bar")
|
||||
assert len(d) == 1, "Only one single value should be retrieved"
|
||||
assert d[0][1] == 'my_other_test_field', "Can't retrieve the created default value. (5)"
|
||||
assert d[0][2] == 'conditional value', "Can't retrieve the created default value. (6)"
|
||||
|
||||
# Do it again but for a specific user.
|
||||
|
||||
ir_values = self.registry('ir.values')
|
||||
ir_values.set(self.cr, self.uid, 'default', False, 'my_test_field',['unexisting_model'], 'specific value', preserve_user=True)
|
||||
|
||||
# Retrieve it and check it is the one for the current user.
|
||||
ir_values = self.registry('ir.values')
|
||||
d = ir_values.get(self.cr, self.uid, 'default', False, ['unexisting_model'])
|
||||
assert len(d) == 1, "Only one default must be returned per field"
|
||||
assert d[0][1] == 'my_test_field', "Can't retrieve the created default value."
|
||||
assert d[0][2] == 'specific value', "Can't retrieve the created default value."
|
||||
|
||||
# Create some action bindings for a non-existing model.
|
||||
|
||||
ir_values = self.registry('ir.values')
|
||||
ir_values.set(self.cr, self.uid, 'action', 'tree_but_open', 'OnDblClick Action', ['unexisting_model'], 'ir.actions.act_window,10', isobject=True)
|
||||
ir_values.set(self.cr, self.uid, 'action', 'tree_but_open', 'OnDblClick Action 2', ['unexisting_model'], 'ir.actions.act_window,11', isobject=True)
|
||||
ir_values.set(self.cr, self.uid, 'action', 'client_action_multi', 'Side Wizard', ['unexisting_model'], 'ir.actions.act_window,12', isobject=True)
|
||||
ir_values.set(self.cr, self.uid, 'action', 'client_print_multi', 'Nice Report', ['unexisting_model'], 'ir.actions.report.xml,2', isobject=True)
|
||||
ir_values.set(self.cr, self.uid, 'action', 'client_action_relate', 'Related Stuff', ['unexisting_model'], 'ir.actions.act_window,14', isobject=True)
|
||||
|
||||
# Replace one action binding to set a new name.
|
||||
|
||||
ir_values = self.registry('ir.values')
|
||||
ir_values.set(self.cr, self.uid, 'action', 'tree_but_open', 'OnDblClick Action New', ['unexisting_model'], 'ir.actions.act_window,10', isobject=True)
|
||||
|
||||
# Retrieve the action bindings and check they're correct
|
||||
|
||||
ir_values = self.registry('ir.values')
|
||||
actions = ir_values.get(self.cr, self.uid, 'action', 'tree_but_open', ['unexisting_model'])
|
||||
assert len(actions) == 2, "Mismatching number of bound actions"
|
||||
#first action
|
||||
assert len(actions[0]) == 3, "Malformed action definition"
|
||||
assert actions[0][1] == 'OnDblClick Action 2', 'Bound action does not match definition'
|
||||
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 11, 'Bound action does not match definition'
|
||||
#second action - this ones comes last because it was re-created with a different name
|
||||
assert len(actions[1]) == 3, "Malformed action definition"
|
||||
assert actions[1][1] == 'OnDblClick Action New', 'Re-Registering an action should replace it'
|
||||
assert isinstance(actions[1][2], dict) and actions[1][2]['id'] == 10, 'Bound action does not match definition'
|
||||
|
||||
actions = ir_values.get(self.cr, self.uid, 'action', 'client_action_multi', ['unexisting_model'])
|
||||
assert len(actions) == 1, "Mismatching number of bound actions"
|
||||
assert len(actions[0]) == 3, "Malformed action definition"
|
||||
assert actions[0][1] == 'Side Wizard', 'Bound action does not match definition'
|
||||
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 12, 'Bound action does not match definition'
|
||||
|
||||
actions = ir_values.get(self.cr, self.uid, 'action', 'client_print_multi', ['unexisting_model'])
|
||||
assert len(actions) == 1, "Mismatching number of bound actions"
|
||||
assert len(actions[0]) == 3, "Malformed action definition"
|
||||
assert actions[0][1] == 'Nice Report', 'Bound action does not match definition'
|
||||
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 2, 'Bound action does not match definition'
|
||||
|
||||
actions = ir_values.get(self.cr, self.uid, 'action', 'client_action_relate', ['unexisting_model'])
|
||||
assert len(actions) == 1, "Mismatching number of bound actions"
|
||||
assert len(actions[0]) == 3, "Malformed action definition"
|
||||
assert actions[0][1] == 'Related Stuff', 'Bound action does not match definition'
|
||||
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 14, 'Bound action does not match definition'
|
|
@ -50,6 +50,7 @@ import openerp.pooler as pooler
|
|||
import openerp.release as release
|
||||
import openerp.tools as tools
|
||||
import openerp.tools.osutil as osutil
|
||||
import openerp.tools.assertion_report as assertion_report
|
||||
|
||||
from openerp.tools.safe_eval import safe_eval as eval
|
||||
from openerp.tools.translate import _
|
||||
|
@ -94,19 +95,20 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=
|
|||
|
||||
def load_test(module_name, idref, mode):
|
||||
cr.commit()
|
||||
if not tools.config.options['test_disable']:
|
||||
try:
|
||||
threading.currentThread().testing = True
|
||||
_load_data(cr, module_name, idref, mode, 'test')
|
||||
except Exception, e:
|
||||
_logger.exception(
|
||||
'Tests failed to execute in module %s', module_name)
|
||||
finally:
|
||||
threading.currentThread().testing = False
|
||||
if tools.config.options['test_commit']:
|
||||
cr.commit()
|
||||
else:
|
||||
cr.rollback()
|
||||
try:
|
||||
threading.currentThread().testing = True
|
||||
_load_data(cr, module_name, idref, mode, 'test')
|
||||
return True
|
||||
except Exception, e:
|
||||
_logger.error(
|
||||
'module %s: an exception occurred in a test', module_name)
|
||||
return False
|
||||
finally:
|
||||
threading.currentThread().testing = False
|
||||
if tools.config.options['test_commit']:
|
||||
cr.commit()
|
||||
else:
|
||||
cr.rollback()
|
||||
|
||||
def _load_data(cr, module_name, idref, mode, kind):
|
||||
"""
|
||||
|
@ -133,7 +135,7 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=
|
|||
elif ext == '.sql':
|
||||
process_sql_file(cr, fp)
|
||||
elif ext == '.yml':
|
||||
tools.convert_yaml_import(cr, module_name, fp, idref, mode, noupdate)
|
||||
tools.convert_yaml_import(cr, module_name, fp, idref, mode, noupdate, report)
|
||||
else:
|
||||
tools.convert_xml_import(cr, module_name, fp, idref, mode, noupdate, report)
|
||||
finally:
|
||||
|
@ -201,7 +203,14 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=
|
|||
# on demo data. Other tests can be added into the regular
|
||||
# 'data' section, but should probably not alter the data,
|
||||
# as there is no rollback.
|
||||
load_test(module_name, idref, mode)
|
||||
if tools.config.options['test_enable']:
|
||||
report.record_result(load_test(module_name, idref, mode))
|
||||
|
||||
# Run the `fast_suite` and `checks` tests given by the module.
|
||||
if module_name == 'base':
|
||||
# Also run the core tests after the database is created.
|
||||
report.record_result(openerp.modules.module.run_unit_tests('openerp'))
|
||||
report.record_result(openerp.modules.module.run_unit_tests(module_name))
|
||||
|
||||
processed_modules.append(package.name)
|
||||
|
||||
|
@ -282,7 +291,6 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
|
|||
# This is a brand new pool, just created in pooler.get_db_and_pool()
|
||||
pool = pooler.get_pool(cr.dbname)
|
||||
|
||||
report = tools.assertion_report()
|
||||
if 'base' in tools.config['update'] or 'all' in tools.config['update']:
|
||||
cr.execute("update ir_module_module set state=%s where name=%s and state=%s", ('to upgrade', 'base', 'installed'))
|
||||
|
||||
|
@ -295,6 +303,7 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
|
|||
|
||||
# processed_modules: for cleanup step after install
|
||||
# loaded_modules: to avoid double loading
|
||||
report = assertion_report.assertion_report()
|
||||
loaded_modules, processed_modules = load_module_graph(cr, graph, status, perform_checks=(not update_module), report=report)
|
||||
|
||||
if tools.config['load_language']:
|
||||
|
@ -414,7 +423,10 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
|
|||
cr.execute("update ir_module_module set state=%s where state=%s", ('uninstalled', 'to remove',))
|
||||
cr.commit()
|
||||
|
||||
_logger.info('Modules loaded.')
|
||||
if report.failures:
|
||||
_logger.error('At least one test failed when loading the modules.')
|
||||
else:
|
||||
_logger.info('Modules loaded.')
|
||||
finally:
|
||||
cr.close()
|
||||
|
||||
|
|
|
@ -20,9 +20,12 @@
|
|||
#
|
||||
##############################################################################
|
||||
|
||||
import os, sys, imp
|
||||
from os.path import join as opj
|
||||
import imp
|
||||
import itertools
|
||||
import os
|
||||
from os.path import join as opj
|
||||
import sys
|
||||
import types
|
||||
import zipimport
|
||||
|
||||
import openerp
|
||||
|
@ -460,5 +463,124 @@ def get_modules_with_version():
|
|||
continue
|
||||
return res
|
||||
|
||||
def get_test_modules(module, submodule, explode):
|
||||
"""
|
||||
Return a list of submodules containing tests.
|
||||
`submodule` can be:
|
||||
- None
|
||||
- the name of a submodule
|
||||
- '__fast_suite__'
|
||||
- '__sanity_checks__'
|
||||
"""
|
||||
# Turn command-line module, submodule into importable names.
|
||||
if module is None:
|
||||
pass
|
||||
elif module == 'openerp':
|
||||
module = 'openerp.tests'
|
||||
else:
|
||||
module = 'openerp.addons.' + module + '.tests'
|
||||
|
||||
# Try to import the module
|
||||
try:
|
||||
__import__(module)
|
||||
except Exception, e:
|
||||
if explode:
|
||||
print 'Can not `import %s`.' % module
|
||||
import logging
|
||||
logging.exception('')
|
||||
sys.exit(1)
|
||||
else:
|
||||
if str(e) == 'No module named tests':
|
||||
# It seems the module has no `tests` sub-module, no problem.
|
||||
pass
|
||||
else:
|
||||
print 'Can not `import %s`.' % module
|
||||
return []
|
||||
|
||||
# Discover available test sub-modules.
|
||||
m = sys.modules[module]
|
||||
submodule_names = sorted([x for x in dir(m) \
|
||||
if x.startswith('test_') and \
|
||||
isinstance(getattr(m, x), types.ModuleType)])
|
||||
submodules = [getattr(m, x) for x in submodule_names]
|
||||
|
||||
def show_submodules_and_exit():
|
||||
if submodule_names:
|
||||
print 'Available submodules are:'
|
||||
for x in submodule_names:
|
||||
print ' ', x
|
||||
sys.exit(1)
|
||||
|
||||
if submodule is None:
|
||||
# Use auto-discovered sub-modules.
|
||||
ms = submodules
|
||||
elif submodule == '__fast_suite__':
|
||||
# Obtain the explicit test sub-modules list.
|
||||
ms = getattr(sys.modules[module], 'fast_suite', None)
|
||||
# `suite` was used before the 6.1 release instead of `fast_suite`.
|
||||
ms = ms if ms else getattr(sys.modules[module], 'suite', None)
|
||||
if ms is None:
|
||||
if explode:
|
||||
print 'The module `%s` has no defined test suite.' % (module,)
|
||||
show_submodules_and_exit()
|
||||
else:
|
||||
ms = []
|
||||
elif submodule == '__sanity_checks__':
|
||||
ms = getattr(sys.modules[module], 'checks', None)
|
||||
if ms is None:
|
||||
if explode:
|
||||
print 'The module `%s` has no defined sanity checks.' % (module,)
|
||||
show_submodules_and_exit()
|
||||
else:
|
||||
ms = []
|
||||
else:
|
||||
# Pick the command-line-specified test sub-module.
|
||||
m = getattr(sys.modules[module], submodule, None)
|
||||
ms = [m]
|
||||
|
||||
if m is None:
|
||||
if explode:
|
||||
print 'The module `%s` has no submodule named `%s`.' % \
|
||||
(module, submodule)
|
||||
show_submodules_and_exit()
|
||||
else:
|
||||
ms = []
|
||||
|
||||
return ms
|
||||
|
||||
def run_unit_tests(module_name):
|
||||
"""
|
||||
Return True or False if some tests were found and succeeded or failed.
|
||||
Return None if no test was found.
|
||||
"""
|
||||
import unittest2
|
||||
ms = get_test_modules(module_name, '__fast_suite__', explode=False)
|
||||
ms.extend(get_test_modules(module_name, '__sanity_checks__', explode=False))
|
||||
suite = unittest2.TestSuite()
|
||||
for m in ms:
|
||||
suite.addTests(unittest2.TestLoader().loadTestsFromModule(m))
|
||||
if ms:
|
||||
_logger.info('module %s: executing %s `fast_suite` and/or `checks` sub-modules', module_name, len(ms))
|
||||
# Use a custom stream object to log the test executions.
|
||||
class MyStream(object):
|
||||
def __init__(self):
|
||||
self.r = re.compile(r'^-*$|^ *... *$|^ok$')
|
||||
def flush(self):
|
||||
pass
|
||||
def write(self, s):
|
||||
if self.r.match(s):
|
||||
return
|
||||
first = True
|
||||
for c in s.split('\n'):
|
||||
if not first:
|
||||
c = '` ' + c
|
||||
first = False
|
||||
_logger.log(logging.TEST, c)
|
||||
result = unittest2.TextTestRunner(verbosity=2, stream=MyStream()).run(suite)
|
||||
if result.wasSuccessful():
|
||||
return True
|
||||
else:
|
||||
_logger.error('module %s: at least one error occurred in a test', module_name)
|
||||
return False
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -48,6 +48,8 @@ import openerp.wsgi
|
|||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
# TODO block until the server is really up, accepting connections
|
||||
# TODO be idemptotent (as long as stop_service was not called).
|
||||
def start_services():
|
||||
""" Start all services.
|
||||
|
||||
|
|
|
@ -1,15 +1,24 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import unittest2
|
||||
"""
|
||||
Tests for the OpenERP library.
|
||||
|
||||
import test_orm
|
||||
This module groups a few sub-modules containing unittest2 test cases.
|
||||
|
||||
Tests can be explicitely added to the `fast_suite` or `checks` lists or not.
|
||||
See the :ref:`test-framework` section in the :ref:`features` list.
|
||||
"""
|
||||
|
||||
import test_expression
|
||||
import test_ir_sequence
|
||||
import test_xmlrpc
|
||||
import test_orm
|
||||
|
||||
# Explicit declaration list of test sub-modules.
|
||||
suite = [
|
||||
test_xmlrpc, # Creates a database
|
||||
test_ir_sequence, # Assume an existing database
|
||||
test_orm, # Assume an existing database
|
||||
fast_suite = [
|
||||
test_ir_sequence,
|
||||
]
|
||||
|
||||
checks = [
|
||||
test_expression,
|
||||
test_orm,
|
||||
]
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import time
|
||||
import unittest2
|
||||
import xmlrpclib
|
||||
|
||||
import openerp
|
||||
|
@ -16,15 +17,6 @@ ADMIN_USER = 'admin'
|
|||
ADMIN_USER_ID = 1
|
||||
ADMIN_PASSWORD = 'admin'
|
||||
|
||||
common_proxy_60 = None
|
||||
db_proxy_60 = None
|
||||
object_proxy_60 = None
|
||||
|
||||
common_proxy_61 = None
|
||||
db_proxy_61 = None
|
||||
model_proxy_61 = None
|
||||
model_uri_61 = None
|
||||
|
||||
def start_openerp():
|
||||
"""
|
||||
Start the OpenERP server similary to the openerp-server script.
|
||||
|
@ -34,33 +26,59 @@ def start_openerp():
|
|||
# Ugly way to ensure the server is listening.
|
||||
time.sleep(2)
|
||||
|
||||
def create_xmlrpc_proxies():
|
||||
def stop_openerp():
|
||||
"""
|
||||
setup some xmlrpclib proxies.
|
||||
Shutdown the OpenERP server similarly to a single ctrl-c.
|
||||
"""
|
||||
global common_proxy_60
|
||||
global db_proxy_60
|
||||
global object_proxy_60
|
||||
|
||||
# Use the old (pre 6.1) API.
|
||||
url = 'http://%s:%d/xmlrpc/' % (HOST, PORT)
|
||||
common_proxy_60 = xmlrpclib.ServerProxy(url + 'common')
|
||||
db_proxy_60 = xmlrpclib.ServerProxy(url + 'db')
|
||||
object_proxy_60 = xmlrpclib.ServerProxy(url + 'object')
|
||||
|
||||
global common_proxy_61
|
||||
global db_proxy_61
|
||||
global model_proxy_61
|
||||
global model_uri_61
|
||||
|
||||
# Use the new (6.1) API.
|
||||
model_uri_61 = 'http://%s:%d/openerp/xmlrpc/1/' % (HOST, PORT)
|
||||
common_proxy_61 = xmlrpclib.ServerProxy(model_uri_61 + 'common')
|
||||
db_proxy_61 = xmlrpclib.ServerProxy(model_uri_61 + 'db')
|
||||
model_proxy_61 = xmlrpclib.ServerProxy(model_uri_61 + 'model/' + DB)
|
||||
|
||||
def tearDownModule():
|
||||
""" Shutdown the OpenERP server similarly to a single ctrl-c. """
|
||||
openerp.service.stop_services()
|
||||
|
||||
class TransactionCase(unittest2.TestCase):
|
||||
"""
|
||||
Subclass of TestCase with a single transaction, rolled-back at the end of
|
||||
the tests.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.cr = openerp.modules.registry.RegistryManager.get(DB).db.cursor()
|
||||
self.uid = openerp.SUPERUSER_ID
|
||||
|
||||
def tearDown(self):
|
||||
self.cr.rollback()
|
||||
self.cr.close()
|
||||
|
||||
def registry(self, model):
|
||||
return openerp.modules.registry.RegistryManager.get(DB)[model]
|
||||
|
||||
class RpcCase(unittest2.TestCase):
|
||||
"""
|
||||
Subclass of TestCase with a few XML-RPC proxies.
|
||||
"""
|
||||
|
||||
def __init__(self, name):
|
||||
super(RpcCase, self).__init__(name)
|
||||
|
||||
class A(object):
|
||||
pass
|
||||
self.proxy = A()
|
||||
|
||||
# Use the old (pre 6.1) API.
|
||||
self.proxy.url_60 = url_60 = 'http://%s:%d/xmlrpc/' % (HOST, PORT)
|
||||
self.proxy.common_60 = xmlrpclib.ServerProxy(url_60 + 'common')
|
||||
self.proxy.db_60 = xmlrpclib.ServerProxy(url_60 + 'db')
|
||||
self.proxy.object_60 = xmlrpclib.ServerProxy(url_60 + 'object')
|
||||
|
||||
# Use the new (6.1) API.
|
||||
self.proxy.url_61 = url_61 = 'http://%s:%d/openerp/xmlrpc/1/' % (HOST, PORT)
|
||||
self.proxy.common_61 = xmlrpclib.ServerProxy(url_61 + 'common')
|
||||
self.proxy.db_61 = xmlrpclib.ServerProxy(url_61 + 'db')
|
||||
self.proxy.model_61 = xmlrpclib.ServerProxy(url_61 + 'model/' + DB)
|
||||
|
||||
@classmethod
|
||||
def generate_database_name(cls):
|
||||
if hasattr(cls, '_database_id'):
|
||||
cls._database_id += 1
|
||||
else:
|
||||
cls._database_id = 0
|
||||
return '_fresh_name_' + str(cls._database_id) + '_'
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import unittest2
|
||||
|
||||
import openerp
|
||||
|
||||
class test_domain_normalization(unittest2.TestCase):
|
||||
def test_normalize_domain(self):
|
||||
expression = openerp.osv.expression
|
||||
norm_domain = domain = ['&',(1,'=',1),('a','=','b')]
|
||||
assert norm_domain == expression.normalize(domain), "Normalized domains should be left untouched"
|
||||
domain = [('x','in',['y','z']),('a.v','=','e'),'|','|',('a','=','b'),'!',('c','>','d'),('e','!=','f'),('g','=','h')]
|
||||
norm_domain = ['&','&','&'] + domain
|
||||
assert norm_domain == expression.normalize(domain), "Non-normalized domains should be properly normalized"
|
|
@ -16,11 +16,6 @@ import common
|
|||
DB = common.DB
|
||||
ADMIN_USER_ID = common.ADMIN_USER_ID
|
||||
|
||||
def setUpModule():
|
||||
common.create_xmlrpc_proxies()
|
||||
|
||||
tearDownModule = common.tearDownModule
|
||||
|
||||
def registry(model):
|
||||
return openerp.modules.registry.RegistryManager.get(DB)[model]
|
||||
|
||||
|
@ -174,8 +169,7 @@ class test_ir_sequence_generate(unittest2.TestCase):
|
|||
def test_ir_sequence_create_no_gap(self):
|
||||
""" Try to create a sequence object. """
|
||||
cr = cursor()
|
||||
d = dict(code='test_sequence_type_6', name='Test sequence type',
|
||||
implementation='no_gap')
|
||||
d = dict(code='test_sequence_type_6', name='Test sequence type')
|
||||
c = registry('ir.sequence.type').create(cr, ADMIN_USER_ID, d, {})
|
||||
assert c
|
||||
d = dict(code='test_sequence_type_6', name='Test sequence')
|
||||
|
|
|
@ -2,9 +2,10 @@ import os
|
|||
import unittest2
|
||||
|
||||
import openerp
|
||||
import common
|
||||
|
||||
UID = 1
|
||||
DB = openerp.tools.config['db_name']
|
||||
UID = common.ADMIN_USER_ID
|
||||
DB = common.DB
|
||||
|
||||
CREATE = lambda values: (0, False, values)
|
||||
UPDATE = lambda id, values: (1, id, values)
|
||||
|
@ -14,16 +15,12 @@ LINK_TO = lambda id: (4, id, False)
|
|||
DELETE_ALL = lambda: (5, False, False)
|
||||
REPLACE_WITH = lambda ids: (6, False, ids)
|
||||
|
||||
class TestO2MSerialization(unittest2.TestCase):
|
||||
class TestO2MSerialization(common.TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
self.cr = openerp.modules.registry.RegistryManager.get(DB).db.cursor()
|
||||
self.partner = openerp.modules.registry.RegistryManager.get(DB)['res.partner']
|
||||
self.address = openerp.modules.registry.RegistryManager.get(DB)['res.partner.address']
|
||||
|
||||
def tearDown(self):
|
||||
self.cr.rollback()
|
||||
self.cr.close()
|
||||
super(TestO2MSerialization, self).setUp()
|
||||
self.partner = self.registry('res.partner')
|
||||
self.address = self.registry('res.partner.address')
|
||||
|
||||
def test_no_command(self):
|
||||
" empty list of commands yields an empty list of records "
|
||||
|
|
|
@ -13,56 +13,64 @@ import xmlrpclib
|
|||
import openerp
|
||||
import common
|
||||
|
||||
DB = common.DB
|
||||
DB = None
|
||||
ADMIN_USER = common.ADMIN_USER
|
||||
ADMIN_USER_ID = common.ADMIN_USER_ID
|
||||
ADMIN_PASSWORD = common.ADMIN_PASSWORD
|
||||
|
||||
def setUpModule():
|
||||
common.start_openerp()
|
||||
common.create_xmlrpc_proxies()
|
||||
common.start_openerp()
|
||||
global DB
|
||||
DB = common.RpcCase.generate_database_name()
|
||||
|
||||
tearDownModule = common.tearDownModule
|
||||
tearDownModule = common.stop_openerp
|
||||
|
||||
class test_xmlrpc(unittest2.TestCase):
|
||||
class test_xmlrpc(common.RpcCase):
|
||||
|
||||
def test_00_xmlrpc_create_database_polling(self):
|
||||
"""
|
||||
Simulate a OpenERP client requesting the creation of a database and
|
||||
polling the server until the creation is complete.
|
||||
"""
|
||||
progress_id = common.db_proxy_60.create(ADMIN_PASSWORD, DB, True,
|
||||
False, ADMIN_PASSWORD)
|
||||
progress_id = self.proxy.db_60.create(ADMIN_PASSWORD,DB, True, False,
|
||||
ADMIN_PASSWORD)
|
||||
while True:
|
||||
time.sleep(1)
|
||||
progress, users = common.db_proxy_60.get_progress(ADMIN_PASSWORD,
|
||||
progress, users = self.proxy.db_60.get_progress(ADMIN_PASSWORD,
|
||||
progress_id)
|
||||
if progress == 1.0:
|
||||
break
|
||||
|
||||
def test_xmlrpc_login(self):
|
||||
""" Try to login on the common service. """
|
||||
uid = common.common_proxy_60.login(DB, ADMIN_USER, ADMIN_PASSWORD)
|
||||
uid = self.proxy.common_60.login(DB, ADMIN_USER, ADMIN_PASSWORD)
|
||||
assert uid == ADMIN_USER_ID
|
||||
|
||||
def test_xmlrpc_ir_model_search(self):
|
||||
""" Try a search on the object service. """
|
||||
ids = common.object_proxy_60.execute(DB, ADMIN_USER_ID, ADMIN_PASSWORD,
|
||||
ids = self.proxy.object_60.execute(DB, ADMIN_USER_ID, ADMIN_PASSWORD,
|
||||
'ir.model', 'search', [])
|
||||
assert ids
|
||||
ids = common.object_proxy_60.execute(DB, ADMIN_USER_ID, ADMIN_PASSWORD,
|
||||
ids = self.proxy.object_60.execute(DB, ADMIN_USER_ID, ADMIN_PASSWORD,
|
||||
'ir.model', 'search', [], {})
|
||||
assert ids
|
||||
|
||||
def test_xmlrpc_61_ir_model_search(self):
|
||||
""" Try a search on the object service. """
|
||||
|
||||
proxy = xmlrpclib.ServerProxy(common.model_uri_61 + 'model/' + DB + '/ir.model')
|
||||
proxy = xmlrpclib.ServerProxy(self.proxy.url_61 + 'model/' + DB +
|
||||
'/ir.model')
|
||||
ids = proxy.execute(ADMIN_USER_ID, ADMIN_PASSWORD, 'search', [])
|
||||
assert ids
|
||||
ids = proxy.execute(ADMIN_USER_ID, ADMIN_PASSWORD, 'search', [], {})
|
||||
assert ids
|
||||
|
||||
def test_zz_xmlrpc_drop_database(self):
|
||||
"""
|
||||
Simulate a OpenERP client requesting the deletion of a database.
|
||||
"""
|
||||
assert self.proxy.db_60.drop(ADMIN_PASSWORD, DB) is True
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest2.main()
|
||||
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
|
||||
class assertion_report(object):
|
||||
"""
|
||||
Simple pair of success and failures counts (used to record YAML and XML
|
||||
`assert` tags as well as unittest2 tests outcome (in this case, not
|
||||
individual `assert`)).
|
||||
"""
|
||||
def __init__(self):
|
||||
self.successes = 0
|
||||
self.failures = 0
|
||||
|
||||
def record_success(self):
|
||||
self.successes += 1
|
||||
|
||||
def record_failure(self):
|
||||
self.failures += 1
|
||||
|
||||
def record_result(self, result):
|
||||
if result is None:
|
||||
pass
|
||||
elif result is True:
|
||||
self.record_success()
|
||||
elif result is False:
|
||||
self.record_failure()
|
||||
|
||||
def __str__(self):
|
||||
res = 'Assertions report: %s successes, %s failures' % (self.successes, self.failures)
|
||||
return res
|
||||
|
|
@ -168,13 +168,10 @@ class configmanager(object):
|
|||
help="Launch a YML test file.")
|
||||
group.add_option("--test-report-directory", dest="test_report_directory", my_default=False,
|
||||
help="If set, will save sample of all reports in this directory.")
|
||||
group.add_option("--test-disable", action="store_true", dest="test_disable",
|
||||
my_default=False, help="Disable loading test files.")
|
||||
group.add_option("--test-enable", action="store_true", dest="test_enable",
|
||||
my_default=False, help="Enable YAML and unit tests.")
|
||||
group.add_option("--test-commit", action="store_true", dest="test_commit",
|
||||
my_default=False, help="Commit database changes performed by tests.")
|
||||
group.add_option("--assert-exit-level", dest='assert_exit_level', type="choice", choices=self._LOGLEVELS.keys(),
|
||||
my_default='error',
|
||||
help="specify the level at which a failed assertion will stop the server. Accepted values: %s" % (self._LOGLEVELS.keys(),))
|
||||
my_default=False, help="Commit database changes performed by YAML or XML tests.")
|
||||
parser.add_option_group(group)
|
||||
|
||||
# Logging Group
|
||||
|
@ -395,7 +392,7 @@ class configmanager(object):
|
|||
'debug_mode', 'smtp_ssl', 'load_language',
|
||||
'stop_after_init', 'logrotate', 'without_demo', 'netrpc', 'xmlrpc', 'syslog',
|
||||
'list_db', 'xmlrpcs', 'proxy_mode',
|
||||
'test_file', 'test_disable', 'test_commit', 'test_report_directory',
|
||||
'test_file', 'test_enable', 'test_commit', 'test_report_directory',
|
||||
'osv_memory_count_limit', 'osv_memory_age_limit', 'max_cron_threads',
|
||||
'virtual_memory_limit', 'virtual_memory_reset', 'cpu_time_limit', 'unaccent',
|
||||
]
|
||||
|
@ -408,11 +405,6 @@ class configmanager(object):
|
|||
elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER:
|
||||
self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg])
|
||||
|
||||
if opt.assert_exit_level:
|
||||
self.options['assert_exit_level'] = self._LOGLEVELS[opt.assert_exit_level]
|
||||
else:
|
||||
self.options['assert_exit_level'] = self._LOGLEVELS.get(self.options['assert_exit_level']) or int(self.options['assert_exit_level'])
|
||||
|
||||
self.options['root_path'] = os.path.abspath(os.path.expanduser(os.path.expandvars(os.path.dirname(openerp.__file__))))
|
||||
if not self.options['addons_path'] or self.options['addons_path']=='None':
|
||||
self.options['addons_path'] = os.path.join(self.options['root_path'], 'addons')
|
||||
|
@ -582,7 +574,7 @@ class configmanager(object):
|
|||
continue
|
||||
if opt in self.blacklist_for_save:
|
||||
continue
|
||||
if opt in ('log_level', 'assert_exit_level'):
|
||||
if opt in ('log_level',):
|
||||
p.set('options', opt, loglevelnames.get(self.options[opt], self.options[opt]))
|
||||
else:
|
||||
p.set('options', opt, self.options[opt])
|
||||
|
|
|
@ -30,6 +30,8 @@ import re
|
|||
import time
|
||||
import openerp.release as release
|
||||
|
||||
import assertion_report
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
|
@ -200,35 +202,6 @@ escape_re = re.compile(r'(?<!\\)/')
|
|||
def escape(x):
|
||||
return x.replace('\\/', '/')
|
||||
|
||||
class assertion_report(object):
|
||||
def __init__(self):
|
||||
self._report = {}
|
||||
|
||||
def record_assertion(self, success, severity):
|
||||
"""
|
||||
Records the result of an assertion for the failed/success count
|
||||
returns success
|
||||
"""
|
||||
if severity in self._report:
|
||||
self._report[severity][success] += 1
|
||||
else:
|
||||
self._report[severity] = {success:1, not success: 0}
|
||||
return success
|
||||
|
||||
def get_report(self):
|
||||
return self._report
|
||||
|
||||
def __str__(self):
|
||||
res = '\nAssertions report:\nLevel\tsuccess\tfailed\n'
|
||||
success = failed = 0
|
||||
for sev in self._report:
|
||||
res += sev + '\t' + str(self._report[sev][True]) + '\t' + str(self._report[sev][False]) + '\n'
|
||||
success += self._report[sev][True]
|
||||
failed += self._report[sev][False]
|
||||
res += 'total\t' + str(success) + '\t' + str(failed) + '\n'
|
||||
res += 'end of report (' + str(success + failed) + ' assertion(s) checked)'
|
||||
return res
|
||||
|
||||
class xml_import(object):
|
||||
@staticmethod
|
||||
def nodeattr2bool(node, attr, default=False):
|
||||
|
@ -712,7 +685,6 @@ form: module.record_id""" % (xml_id,)
|
|||
rec_src = rec.get("search",'').encode('utf8')
|
||||
rec_src_count = rec.get("count")
|
||||
|
||||
severity = rec.get("severity",'').encode('ascii') or loglevels.LOG_ERROR
|
||||
rec_string = rec.get("string",'').encode('utf8') or 'unknown'
|
||||
|
||||
ids = None
|
||||
|
@ -727,17 +699,13 @@ form: module.record_id""" % (xml_id,)
|
|||
if rec_src_count:
|
||||
count = int(rec_src_count)
|
||||
if len(ids) != count:
|
||||
self.assert_report.record_assertion(False, severity)
|
||||
self.assertion_report.record_failure()
|
||||
msg = 'assertion "%s" failed!\n' \
|
||||
' Incorrect search count:\n' \
|
||||
' expected count: %d\n' \
|
||||
' obtained count: %d\n' \
|
||||
% (rec_string, count, len(ids))
|
||||
sevval = getattr(logging, severity.upper())
|
||||
_logger.log(sevval, msg)
|
||||
if sevval >= config['assert_exit_level']:
|
||||
# TODO: define a dedicated exception
|
||||
raise Exception('Severe assertion failure')
|
||||
_logger.error(msg)
|
||||
return
|
||||
|
||||
assert ids is not None,\
|
||||
|
@ -759,20 +727,16 @@ form: module.record_id""" % (xml_id,)
|
|||
expected_value = _eval_xml(self, test, self.pool, cr, uid, self.idref, context=context) or True
|
||||
expression_value = unsafe_eval(f_expr, globals_dict)
|
||||
if expression_value != expected_value: # assertion failed
|
||||
self.assert_report.record_assertion(False, severity)
|
||||
self.assertion_report.record_failure()
|
||||
msg = 'assertion "%s" failed!\n' \
|
||||
' xmltag: %s\n' \
|
||||
' expected value: %r\n' \
|
||||
' obtained value: %r\n' \
|
||||
% (rec_string, etree.tostring(test), expected_value, expression_value)
|
||||
sevval = getattr(logging, severity.upper())
|
||||
_logger.log(sevval, msg)
|
||||
if sevval >= config['assert_exit_level']:
|
||||
# TODO: define a dedicated exception
|
||||
raise Exception('Severe assertion failure')
|
||||
_logger.error(msg)
|
||||
return
|
||||
else: # all tests were successful for this assertion tag (no break)
|
||||
self.assert_report.record_assertion(True, severity)
|
||||
self.assertion_report.record_success()
|
||||
|
||||
def _tag_record(self, cr, rec, data_node=None):
|
||||
rec_model = rec.get("model").encode('ascii')
|
||||
|
@ -906,8 +870,8 @@ form: module.record_id""" % (xml_id,)
|
|||
self.pool = pooler.get_pool(cr.dbname)
|
||||
self.uid = 1
|
||||
if report is None:
|
||||
report = assertion_report()
|
||||
self.assert_report = report
|
||||
report = assertion_report.assertion_report()
|
||||
self.assertion_report = report
|
||||
self.noupdate = noupdate
|
||||
self._tags = {
|
||||
'menuitem': self._tag_menuitem,
|
||||
|
|
|
@ -19,6 +19,8 @@ from lxml import etree
|
|||
unsafe_eval = eval
|
||||
from safe_eval import safe_eval as eval
|
||||
|
||||
import assertion_report
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class YamlImportException(Exception):
|
||||
|
@ -85,33 +87,6 @@ def is_ir_set(node):
|
|||
def is_string(node):
|
||||
return isinstance(node, basestring)
|
||||
|
||||
class TestReport(object):
|
||||
def __init__(self):
|
||||
self._report = {}
|
||||
|
||||
def record(self, success, severity):
|
||||
"""
|
||||
Records the result of an assertion for the failed/success count.
|
||||
Returns success.
|
||||
"""
|
||||
if severity in self._report:
|
||||
self._report[severity][success] += 1
|
||||
else:
|
||||
self._report[severity] = {success: 1, not success: 0}
|
||||
return success
|
||||
|
||||
def __str__(self):
|
||||
res = []
|
||||
res.append('\nAssertions report:\nLevel\tsuccess\tfailure')
|
||||
success = failure = 0
|
||||
for severity in self._report:
|
||||
res.append("%s\t%s\t%s" % (severity, self._report[severity][True], self._report[severity][False]))
|
||||
success += self._report[severity][True]
|
||||
failure += self._report[severity][False]
|
||||
res.append("total\t%s\t%s" % (success, failure))
|
||||
res.append("end of report (%s assertion(s) checked)" % (success + failure))
|
||||
return "\n".join(res)
|
||||
|
||||
class RecordDictWrapper(dict):
|
||||
"""
|
||||
Used to pass a record as locals in eval:
|
||||
|
@ -125,13 +100,15 @@ class RecordDictWrapper(dict):
|
|||
return dict.__getitem__(self, key)
|
||||
|
||||
class YamlInterpreter(object):
|
||||
def __init__(self, cr, module, id_map, mode, filename, noupdate=False):
|
||||
def __init__(self, cr, module, id_map, mode, filename, report=None, noupdate=False):
|
||||
self.cr = cr
|
||||
self.module = module
|
||||
self.id_map = id_map
|
||||
self.mode = mode
|
||||
self.filename = filename
|
||||
self.assert_report = TestReport()
|
||||
if report is None:
|
||||
report = assertion_report.assertion_report()
|
||||
self.assertion_report = report
|
||||
self.noupdate = noupdate
|
||||
self.pool = pooler.get_pool(cr.dbname)
|
||||
self.uid = 1
|
||||
|
@ -210,18 +187,9 @@ class YamlInterpreter(object):
|
|||
def process_comment(self, node):
|
||||
return node
|
||||
|
||||
def _log_assert_failure(self, severity, msg, *args):
|
||||
if isinstance(severity, types.StringTypes):
|
||||
levelname = severity.strip().upper()
|
||||
level = logging.getLevelName(levelname)
|
||||
else:
|
||||
level = severity
|
||||
levelname = logging.getLevelName(level)
|
||||
self.assert_report.record(False, levelname)
|
||||
_logger.log(level, msg, *args)
|
||||
if level >= config['assert_exit_level']:
|
||||
raise YamlImportAbortion('Severe assertion failure (%s), aborting.' % levelname)
|
||||
return
|
||||
def _log_assert_failure(self, msg, *args):
|
||||
self.assertion_report.record_failure()
|
||||
_logger.error(msg, *args)
|
||||
|
||||
def _get_assertion_id(self, assertion):
|
||||
if assertion.id:
|
||||
|
@ -250,7 +218,7 @@ class YamlInterpreter(object):
|
|||
' expected count: %d\n' \
|
||||
' obtained count: %d\n'
|
||||
args = (assertion.string, assertion.count, len(ids))
|
||||
self._log_assert_failure(assertion.severity, msg, *args)
|
||||
self._log_assert_failure(msg, *args)
|
||||
else:
|
||||
context = self.get_context(assertion, self.eval_context)
|
||||
for id in ids:
|
||||
|
@ -283,10 +251,10 @@ class YamlInterpreter(object):
|
|||
args += ( lmsg, aop, rmsg )
|
||||
break
|
||||
|
||||
self._log_assert_failure(assertion.severity, msg, *args)
|
||||
self._log_assert_failure(msg, *args)
|
||||
return
|
||||
else: # all tests were successful for this assertion tag (no break)
|
||||
self.assert_report.record(True, assertion.severity)
|
||||
self.assertion_report.record_success()
|
||||
|
||||
def _coerce_bool(self, value, default=False):
|
||||
if isinstance(value, types.BooleanType):
|
||||
|
@ -528,13 +496,13 @@ class YamlInterpreter(object):
|
|||
code_obj = compile(statements, self.filename, 'exec')
|
||||
unsafe_eval(code_obj, {'ref': self.get_id}, code_context)
|
||||
except AssertionError, e:
|
||||
self._log_assert_failure(python.severity, 'AssertionError in Python code %s: %s', python.name, e)
|
||||
self._log_assert_failure('AssertionError in Python code %s: %s', python.name, e)
|
||||
return
|
||||
except Exception, e:
|
||||
_logger.debug('Exception during evaluation of !python block in yaml_file %s.', self.filename, exc_info=True)
|
||||
raise
|
||||
else:
|
||||
self.assert_report.record(True, python.severity)
|
||||
self.assertion_report.record_success()
|
||||
|
||||
def process_workflow(self, node):
|
||||
workflow, values = node.items()[0]
|
||||
|
@ -827,7 +795,7 @@ class YamlInterpreter(object):
|
|||
"""
|
||||
Empty node or commented node should not pass silently.
|
||||
"""
|
||||
self._log_assert_failure(logging.WARNING, "You have an empty block in your tests.")
|
||||
self._log_assert_failure("You have an empty block in your tests.")
|
||||
|
||||
|
||||
def process(self, yaml_string):
|
||||
|
@ -900,11 +868,11 @@ class YamlInterpreter(object):
|
|||
is_preceded_by_comment = False
|
||||
return is_preceded_by_comment
|
||||
|
||||
def yaml_import(cr, module, yamlfile, idref=None, mode='init', noupdate=False):
|
||||
def yaml_import(cr, module, yamlfile, idref=None, mode='init', noupdate=False, report=None):
|
||||
if idref is None:
|
||||
idref = {}
|
||||
yaml_string = yamlfile.read()
|
||||
yaml_interpreter = YamlInterpreter(cr, module, idref, mode, filename=yamlfile.name, noupdate=noupdate)
|
||||
yaml_interpreter = YamlInterpreter(cr, module, idref, mode, filename=yamlfile.name, report=report, noupdate=noupdate)
|
||||
yaml_interpreter.process(yaml_string)
|
||||
|
||||
# keeps convention of convert.py
|
||||
|
|
|
@ -430,7 +430,7 @@ def serve():
|
|||
else:
|
||||
app = application
|
||||
suffix = ''
|
||||
httpd = werkzeug.serving.make_server(interface, port, application, threaded=True)
|
||||
httpd = werkzeug.serving.make_server(interface, port, app, threaded=True)
|
||||
_logger.info('HTTP service (werkzeug) running on %s:%s%s', interface, port, suffix)
|
||||
except ImportError:
|
||||
import wsgiref.simple_server
|
||||
|
|
Loading…
Reference in New Issue