1# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"). You
4# may not use this file except in compliance with the License. A copy of
5# the License is located at
6#
7# https://aws.amazon.com/apache2.0/
8#
9# or in the "license" file accompanying this file. This file is
10# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11# ANY KIND, either express or implied. See the License for the specific
12# language governing permissions and limitations under the License.
13import pytest
14import botocore.session
15from botocore import xform_name
16from botocore.exceptions import DataNotFoundError
17
18import boto3
19from boto3.docs.service import ServiceDocumenter
20
21
22@pytest.fixture
23def botocore_session():
24    return botocore.session.get_session()
25
26
27@pytest.fixture
28def boto3_session():
29    return boto3.Session(region_name='us-east-1')
30
31
32def all_services():
33    session = boto3.Session(region_name='us-east-1')
34    for service_name in session.get_available_services():
35        yield service_name
36
37
38@pytest.fixture
39def available_resources():
40    session = boto3.Session(region_name='us-east-1')
41    return session.get_available_resources()
42
43
44@pytest.mark.parametrize('service_name', all_services())
45def test_documentation(
46    boto3_session, botocore_session, available_resources, service_name
47):
48    generated_docs = ServiceDocumenter(
49        service_name, session=boto3_session).document_service()
50    generated_docs = generated_docs.decode('utf-8')
51    client = boto3.client(service_name, 'us-east-1')
52
53    # Check that all of the services have the appropriate title
54    _assert_has_title(generated_docs, client)
55
56    # Check that all services have the client documented.
57    _assert_has_client_documentation(generated_docs, service_name, client)
58
59    # If the service has resources, make sure the service resource
60    # is at least documented.
61    if service_name in available_resources:
62
63        resource = boto3.resource(service_name, 'us-east-1')
64        _assert_has_resource_documentation(
65            generated_docs, service_name, resource
66        )
67
68    # If the client can paginate, make sure the paginators are documented.
69    try:
70        paginator_model = botocore_session.get_paginator_model(service_name)
71        _assert_has_paginator_documentation(
72            generated_docs, service_name, client,
73            sorted(paginator_model._paginator_config)
74        )
75    except DataNotFoundError:
76        pass
77
78    # If the client has waiters, make sure the waiters are documented.
79    if client.waiter_names:
80        waiter_model = botocore_session.get_waiter_model(service_name)
81        _assert_has_waiter_documentation(
82            generated_docs, service_name, client, waiter_model
83        )
84
85
86def _assert_contains_lines_in_order(lines, contents):
87    for line in lines:
88        assert line in contents
89        beginning = contents.find(line)
90        contents = contents[(beginning + len(line)):]
91
92
93def _assert_has_title(generated_docs, client):
94    title = client.__class__.__name__
95    ref_lines = [
96        '*' * len(title),
97        title,
98        '*' * len(title)
99    ]
100    _assert_contains_lines_in_order(ref_lines, generated_docs)
101
102
103def _assert_has_client_documentation(generated_docs, service_name, client):
104    class_name = client.__class__.__name__
105    ref_lines = [
106        '======',
107        'Client',
108        '======',
109        '.. py:class:: %s.Client' % class_name,
110        '  A low-level client representing',
111        '    import boto3',
112        '    client = boto3.client(\'%s\')' % service_name,
113        '  These are the available methods:',
114        '  *   :py:meth:`~%s.Client.get_paginator`' % class_name,
115        '  *   :py:meth:`~%s.Client.get_waiter`' % class_name,
116        '  .. py:method:: get_paginator(operation_name)',
117        '  .. py:method:: get_waiter(waiter_name)',
118    ]
119    _assert_contains_lines_in_order(ref_lines, generated_docs)
120
121
122def _assert_has_paginator_documentation(generated_docs, service_name, client,
123                                        paginator_names):
124    ref_lines = [
125        '==========',
126        'Paginators',
127        '==========',
128        'The available paginators are:'
129    ]
130    for paginator_name in paginator_names:
131        ref_lines.append(
132            '* :py:class:`%s.Paginator.%s`' % (
133                client.__class__.__name__, paginator_name))
134
135    for paginator_name in paginator_names:
136        ref_lines.append(
137            '.. py:class:: %s.Paginator.%s' % (
138                client.__class__.__name__, paginator_name))
139        ref_lines.append(
140            '  .. py:method:: paginate(')
141
142    _assert_contains_lines_in_order(ref_lines, generated_docs)
143
144
145def _assert_has_waiter_documentation(generated_docs, service_name, client,
146                                     waiter_model):
147    ref_lines = [
148        '=======',
149        'Waiters',
150        '=======',
151        'The available waiters are:'
152    ]
153    for waiter_name in waiter_model.waiter_names:
154        ref_lines.append(
155            '* :py:class:`%s.Waiter.%s`' % (
156                client.__class__.__name__, waiter_name))
157
158    for waiter_name in waiter_model.waiter_names:
159        ref_lines.append(
160            '.. py:class:: %s.Waiter.%s' % (
161                client.__class__.__name__, waiter_name))
162        ref_lines.append(
163            '    waiter = client.get_waiter(\'%s\')' % xform_name(waiter_name))
164        ref_lines.append(
165            '  .. py:method:: wait(')
166
167    _assert_contains_lines_in_order(ref_lines, generated_docs)
168
169
170def _assert_has_resource_documentation(generated_docs, service_name, resource):
171    ref_lines = [
172        '================',
173        'Service Resource',
174        '================',
175        '.. py:class:: %s.ServiceResource' % (
176            resource.meta.client.__class__.__name__),
177        '  A resource representing',
178        '    import boto3',
179        '    %s = boto3.resource(\'%s\')' % (service_name, service_name),
180    ]
181    _assert_contains_lines_in_order(ref_lines, generated_docs)
182