2 * Copyright 2000-2015 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.jetbrains.python;
18 import com.intellij.psi.PsiElement;
19 import com.intellij.psi.search.PsiElementProcessor;
20 import com.intellij.psi.util.PsiTreeUtil;
21 import com.jetbrains.python.documentation.docstrings.DocStringUtil;
22 import com.jetbrains.python.documentation.docstrings.GoogleCodeStyleDocString;
23 import com.jetbrains.python.documentation.docstrings.NumpyDocString;
24 import com.jetbrains.python.documentation.docstrings.SectionBasedDocString;
25 import com.jetbrains.python.documentation.docstrings.SectionBasedDocString.Section;
26 import com.jetbrains.python.documentation.docstrings.SectionBasedDocString.SectionField;
27 import com.jetbrains.python.fixtures.PyTestCase;
28 import com.jetbrains.python.psi.PyStringLiteralExpression;
29 import com.jetbrains.python.toolbox.Substring;
30 import org.jetbrains.annotations.NotNull;
31 import org.jetbrains.annotations.Nullable;
33 import java.util.List;
36 * @author Mikhail Golubev
38 public class PySectionBasedDocStringTest extends PyTestCase {
40 public void testSimpleGoogleDocString() {
41 checkSimpleDocstringStructure(findAndParseGoogleStyleDocString());
44 public void testSimpleNumpyDocstring() {
45 checkSimpleDocstringStructure(findAndParseNumpyStyleDocString());
48 private static void checkSimpleDocstringStructure(@NotNull SectionBasedDocString docString) {
49 assertEquals("Summary", docString.getSummary());
50 final List<Section> sections = docString.getSections();
51 assertSize(3, sections);
53 assertEquals("parameters", sections.get(0).getNormalizedTitle());
54 final List<SectionField> paramFields = sections.get(0).getFields();
55 assertSize(2, paramFields);
56 final SectionField param1 = paramFields.get(0);
57 assertEquals("x", param1.getName());
58 assertEquals("int", param1.getType());
59 assertEquals("first parameter", param1.getDescription());
61 final SectionField param2 = paramFields.get(1);
62 assertEquals("y", param2.getName());
63 assertEmpty(param2.getType());
64 assertEquals("second parameter\n" +
65 "with longer description", param2.getDescription());
67 assertEquals("raises", sections.get(1).getNormalizedTitle());
68 final List<SectionField> exceptionFields = sections.get(1).getFields();
69 assertSize(1, exceptionFields);
70 final SectionField exception1 = exceptionFields.get(0);
71 assertEmpty(exception1.getName());
72 assertEquals("Exception", exception1.getType());
73 assertEquals("if anything bad happens", exception1.getDescription());
75 assertEquals("returns", sections.get(2).getNormalizedTitle());
76 final List<SectionField> returnFields = sections.get(2).getFields();
77 assertSize(1, returnFields);
78 final SectionField return1 = returnFields.get(0);
79 assertEmpty(return1.getName());
80 assertEquals("None", return1.getType());
81 assertEquals("always", return1.getDescription());
85 private Substring findAndParseDocString() {
86 myFixture.configureByFile(getTestName(true) + ".py");
87 final String docStringText = findFirstDocString();
89 assertNotNull(docStringText);
90 return new Substring(docStringText);
94 private GoogleCodeStyleDocString findAndParseGoogleStyleDocString() {
95 return new GoogleCodeStyleDocString(findAndParseDocString());
99 private NumpyDocString findAndParseNumpyStyleDocString() {
100 return new NumpyDocString(findAndParseDocString());
104 private String findFirstDocString() {
105 final PsiElementProcessor.FindElement<PsiElement> processor = new PsiElementProcessor.FindElement<PsiElement>() {
107 public boolean execute(@NotNull PsiElement element) {
108 if (element instanceof PyStringLiteralExpression && element.getFirstChild().getNode().getElementType() == PyTokenTypes.DOCSTRING) {
109 return setFound(element);
114 PsiTreeUtil.processElements(myFixture.getFile(), processor);
115 if (!processor.isFound()) {
118 final PsiElement foundElement = processor.getFoundElement();
119 assertNotNull(foundElement);
120 return ((PyStringLiteralExpression)foundElement).getStringValue();
123 public void testSectionStartAfterQuotes() {
124 final GoogleCodeStyleDocString docString = findAndParseGoogleStyleDocString();
125 assertEmpty(docString.getSummary());
127 assertSize(2, docString.getSections());
129 final Section examplesSection = docString.getSections().get(0);
130 assertEquals("examples", examplesSection.getNormalizedTitle());
131 assertSize(1, examplesSection.getFields());
132 final SectionField example1 = examplesSection.getFields().get(0);
133 assertEmpty(example1.getName());
134 assertEmpty(example1.getType());
135 assertEquals("Useless call\n" +
136 "func() == func()", example1.getDescription());
138 final Section notesSection = docString.getSections().get(1);
139 assertEquals("notes", notesSection.getNormalizedTitle());
140 assertSize(1, notesSection.getFields());
141 final SectionField note1 = notesSection.getFields().get(0);
142 assertEmpty(note1.getName());
143 assertEmpty(note1.getType());
144 assertEquals("some\n" +
145 "notes", note1.getDescription());
148 public void testTypeReferences() {
149 final GoogleCodeStyleDocString docString = findAndParseGoogleStyleDocString();
150 assertEmpty(docString.getSummary());
151 assertSize(2, docString.getSections());
153 final Section paramSection = docString.getSections().get(0);
154 assertSize(1, paramSection.getFields());
155 final SectionField param1 = paramSection.getFields().get(0);
156 assertEquals("a1", param1.getName());
157 assertEquals(":class:`MyClass`", param1.getType());
158 assertEquals("used to call :def:`my_function` and access :attr:`my_attr`", param1.getDescription());
160 final Section raisesSection = docString.getSections().get(1);
161 assertSize(1, raisesSection.getFields());
162 final SectionField exception1 = raisesSection.getFields().get(0);
163 assertEmpty(exception1.getName());
164 assertEquals(":class:`MyException`", exception1.getType());
165 assertEquals("thrown in case of any error", exception1.getDescription());
168 public void testNestedIndentation() {
169 final GoogleCodeStyleDocString docString = findAndParseGoogleStyleDocString();
170 assertSize(1, docString.getSections());
171 final Section section1 = docString.getSections().get(0);
172 assertEquals("parameters", section1.getNormalizedTitle());
173 assertSize(1, section1.getFields());
174 final SectionField param1 = section1.getFields().get(0);
175 assertEquals("x", param1.getName());
176 assertEquals("int", param1.getType());
177 assertEquals("first line of the description\n" +
183 " assert func(42) is None", param1.getDescription());
186 public void testMultilineSummary() {
187 final GoogleCodeStyleDocString docString = findAndParseGoogleStyleDocString();
188 assertEquals("First line\n" +
190 "Third line", docString.getSummary());
193 public void testNamedReturnsAndYields() {
194 final GoogleCodeStyleDocString docString = findAndParseGoogleStyleDocString();
195 assertEmpty(docString.getSummary());
196 assertSize(2, docString.getSections());
198 final Section returnSection = docString.getSections().get(0);
199 assertSize(2, returnSection.getFields());
201 final SectionField return1 = returnSection.getFields().get(0);
202 assertEquals("status_code", return1.getName());
203 assertEquals("int", return1.getType());
204 assertEquals("HTTP status code", return1.getDescription());
206 final SectionField return2 = returnSection.getFields().get(1);
207 assertEquals("template", return2.getName());
208 assertEquals("str", return2.getType());
209 assertEquals("path to template in template roots", return2.getDescription());
211 final Section yieldSection = docString.getSections().get(1);
212 assertSize(1, yieldSection.getFields());
213 final SectionField yield1 = yieldSection.getFields().get(0);
214 assertEquals("progress", yield1.getName());
215 assertEquals("float", yield1.getType());
216 assertEquals("floating point value in range [0, 1) indicating progress\n" +
217 "of the task", yield1.getDescription());
220 public void testNumpySignature() {
221 final NumpyDocString docString = findAndParseNumpyStyleDocString();
222 assertEquals("a.diagonal(offset=0, axis1=0, axis2=1)", docString.getSignature());
223 assertEquals("Return specified diagonals.", docString.getSummary());
226 public void testNumpySectionBlockBreaksOnDoubleEmptyLine() {
227 final NumpyDocString docString = findAndParseNumpyStyleDocString();
228 assertSize(1, docString.getSections());
229 final Section paramSection = docString.getSections().get(0);
230 assertEquals("parameters", paramSection.getNormalizedTitle());
231 assertSize(1, paramSection.getFields());
232 final SectionField param1 = paramSection.getFields().get(0);
233 assertEquals("x", param1.getName());
234 assertEmpty(param1.getType());
235 assertEquals("First line\n" +
238 "Line after single break", param1.getDescription());
241 public void testGoogleEmptyParamTypeInParenthesis() {
242 final GoogleCodeStyleDocString docString = findAndParseGoogleStyleDocString();
243 assertSize(1, docString.getSections());
244 final Section paramSection = docString.getSections().get(0);
245 assertEquals("parameters", paramSection.getNormalizedTitle());
246 assertSize(1, paramSection.getFields());
247 final SectionField param1 = paramSection.getFields().get(0);
248 assertEquals("x", param1.getName());
249 assertEmpty(param1.getDescription());
250 assertEmpty(param1.getType());
251 assertNotNull(param1.getTypeAsSubstring());
252 assertEquals(26, param1.getTypeAsSubstring().getStartOffset());
253 assertEquals(26, param1.getTypeAsSubstring().getEndOffset());
256 public void testGoogleReturnTypeNoDescription() {
257 final GoogleCodeStyleDocString docString = findAndParseGoogleStyleDocString();
258 assertSize(1, docString.getSections());
259 final Section returnSection = docString.getSections().get(0);
260 assertEquals("returns", returnSection.getNormalizedTitle());
261 assertSize(1, returnSection.getFields());
262 final SectionField return1 = returnSection.getFields().get(0);
263 assertEmpty(return1.getName());
264 assertEmpty(return1.getDescription());
265 assertEquals("object", return1.getType());
266 assertNotNull(return1.getTypeAsSubstring());
267 assertEquals(20, return1.getTypeAsSubstring().getStartOffset());
268 assertEquals(26, return1.getTypeAsSubstring().getEndOffset());
271 public void testGoogleNoEmptyLineAfterSummary() {
272 final GoogleCodeStyleDocString docString = findAndParseGoogleStyleDocString();
273 assertEquals("Summary.", docString.getSummary());
274 assertSize(1, docString.getSections());
275 assertSize(1, docString.getSections().get(0).getFields());
278 public void testGoogleParametersSectionWithoutSummary() {
279 final GoogleCodeStyleDocString docString = findAndParseGoogleStyleDocString();
280 assertEmpty(docString.getSummary());
281 assertSize(1, docString.getSections());
282 final Section paramSection = docString.getSections().get(0);
283 assertEquals("parameters", paramSection.getNormalizedTitle());
284 assertSize(1, paramSection.getFields());
287 public void testGoogleKeywordArgumentsSection() {
288 final GoogleCodeStyleDocString docString = findAndParseGoogleStyleDocString();
289 assertEmpty(docString.getSummary());
290 assertSize(1, docString.getSections());
291 assertEquals("keyword arguments", docString.getSections().get(0).getNormalizedTitle());
295 public void testGoogleDocStringContentDetection() {
296 assertTrue(DocStringUtil.isLikeGoogleDocString(
299 " some user defined section\n" +
307 public void testNumpyEmptySectionIndent() {
308 final NumpyDocString docString = findAndParseNumpyStyleDocString();
309 assertSize(3, docString.getSections());
310 final Section paramSection = docString.getSections().get(0);
311 assertEquals("parameters", paramSection.getNormalizedTitle());
312 assertSize(2, paramSection.getFields());
313 final Section exampleSection = docString.getSections().get(1);
314 assertSize(1, exampleSection.getFields());
315 assertEquals("First sentence.\n" +
316 "Second sentence.", exampleSection.getFields().get(0).getDescription());
317 final Section returnSection = docString.getSections().get(2);
318 assertSize(1, returnSection.getFields());
319 assertEquals("Something", returnSection.getFields().get(0).getType());
322 public void testGoogleParamNamedLikeSection() {
323 final GoogleCodeStyleDocString docString = findAndParseGoogleStyleDocString();
324 assertSize(1, docString.getSections());
325 final Section paramSection = docString.getSections().get(0);
326 assertSize(2, paramSection.getFields());
327 assertEquals("x", paramSection.getFields().get(0).getName());
328 assertEquals("args", paramSection.getFields().get(1).getName());
331 public void testGoogleNoColonAfterParameter() {
332 final GoogleCodeStyleDocString docString = findAndParseGoogleStyleDocString();
333 assertSize(1, docString.getSections());
334 final Section paramSection = docString.getSections().get(0);
335 assertSize(2, paramSection.getFields());
336 final SectionField x = paramSection.getFields().get(0);
337 assertEquals("x", x.getName());
338 assertEmpty(x.getType());
339 assertEmpty(x.getDescription());
341 final SectionField y = paramSection.getFields().get(1);
342 assertEquals("y", y.getName());
343 assertEquals("int", y.getType());
344 assertEmpty(y.getDescription());
347 public void testNumpyMultipleReturns() {
348 final NumpyDocString docString = findAndParseNumpyStyleDocString();
349 assertSize(1, docString.getSections());
350 final Section returnSection = docString.getSections().get(0);
351 assertSize(2, returnSection.getFields());
355 public void testNumpyCombinedParamDeclarations() {
356 final NumpyDocString docString = findAndParseNumpyStyleDocString();
357 assertSize(1, docString.getSections());
358 final Section paramSection = docString.getSections().get(0);
359 assertSize(1, paramSection.getFields());
360 final SectionField firstField = paramSection.getFields().get(0);
361 assertSameElements(firstField.getNames(), "x", "y", "args", "kwargs");
362 assertEquals(firstField.getType(), "Any");
363 assertEquals(firstField.getDescription(), "description");
367 public void testGoogleMandatoryIndentationInsideSection() {
368 final GoogleCodeStyleDocString docString = findAndParseGoogleStyleDocString();
369 assertSize(3, docString.getSections());
370 assertEmpty(docString.getSections().get(0).getFields());
371 assertSize(1, docString.getSections().get(1).getFields());
372 final Section thirdSection = docString.getSections().get(2);
373 assertSize(1, thirdSection.getFields());
374 final SectionField firstExample = thirdSection.getFields().get(0);
375 assertEmpty(firstExample.getName());
376 assertEmpty(firstExample.getType());
377 assertEquals("first line\n" +
378 "second line", firstExample.getDescription());
382 protected String getTestDataPath() {
383 return super.getTestDataPath() + "/docstrings";