| // Copyright 2023 The Bazel Authors. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package python |
| |
| import ( |
| "context" |
| "testing" |
| |
| "github.com/stretchr/testify/assert" |
| ) |
| |
| func TestParseImportStatements(t *testing.T) { |
| t.Parallel() |
| units := []struct { |
| name string |
| code string |
| filepath string |
| result []module |
| }{ |
| { |
| name: "not has import", |
| code: "a = 1\nb = 2", |
| filepath: "", |
| result: nil, |
| }, |
| { |
| name: "has import", |
| code: "import unittest\nimport os.path\nfrom foo.bar import abc.xyz", |
| filepath: "abc.py", |
| result: []module{ |
| { |
| Name: "unittest", |
| LineNumber: 1, |
| Filepath: "abc.py", |
| From: "", |
| }, |
| { |
| Name: "os.path", |
| LineNumber: 2, |
| Filepath: "abc.py", |
| From: "", |
| }, |
| { |
| Name: "foo.bar.abc.xyz", |
| LineNumber: 3, |
| Filepath: "abc.py", |
| From: "foo.bar", |
| }, |
| }, |
| }, |
| { |
| name: "has import in def", |
| code: `def foo(): |
| import unittest |
| `, |
| filepath: "abc.py", |
| result: []module{ |
| { |
| Name: "unittest", |
| LineNumber: 2, |
| Filepath: "abc.py", |
| From: "", |
| }, |
| }, |
| }, |
| { |
| name: "invalid syntax", |
| code: "import os\nimport", |
| filepath: "abc.py", |
| result: []module{ |
| { |
| Name: "os", |
| LineNumber: 1, |
| Filepath: "abc.py", |
| From: "", |
| }, |
| }, |
| }, |
| { |
| name: "import as", |
| code: "import os as b\nfrom foo import bar as c# 123", |
| filepath: "abc.py", |
| result: []module{ |
| { |
| Name: "os", |
| LineNumber: 1, |
| Filepath: "abc.py", |
| From: "", |
| }, |
| { |
| Name: "foo.bar", |
| LineNumber: 2, |
| Filepath: "abc.py", |
| From: "foo", |
| }, |
| }, |
| }, |
| // align to https://docs.python.org/3/reference/simple_stmts.html#index-34 |
| { |
| name: "complex import", |
| code: "from unittest import *\nfrom foo import (bar as c, baz, qux as d)\nfrom . import abc", |
| result: []module{ |
| { |
| Name: "unittest.*", |
| LineNumber: 1, |
| From: "unittest", |
| }, |
| { |
| Name: "foo.bar", |
| LineNumber: 2, |
| From: "foo", |
| }, |
| { |
| Name: "foo.baz", |
| LineNumber: 2, |
| From: "foo", |
| }, |
| { |
| Name: "foo.qux", |
| LineNumber: 2, |
| From: "foo", |
| }, |
| }, |
| }, |
| } |
| for _, u := range units { |
| t.Run(u.name, func(t *testing.T) { |
| p := NewFileParser() |
| code := []byte(u.code) |
| p.SetCodeAndFile(code, "", u.filepath) |
| output, err := p.Parse(context.Background()) |
| assert.NoError(t, err) |
| assert.Equal(t, u.result, output.Modules) |
| }) |
| } |
| } |
| |
| func TestParseComments(t *testing.T) { |
| t.Parallel() |
| units := []struct { |
| name string |
| code string |
| result []comment |
| }{ |
| { |
| name: "not has comment", |
| code: "a = 1\nb = 2", |
| result: nil, |
| }, |
| { |
| name: "has comment", |
| code: "# a = 1\n# b = 2", |
| result: []comment{"# a = 1", "# b = 2"}, |
| }, |
| { |
| name: "has comment in if", |
| code: "if True:\n # a = 1\n # b = 2", |
| result: []comment{"# a = 1", "# b = 2"}, |
| }, |
| { |
| name: "has comment inline", |
| code: "import os# 123\nfrom pathlib import Path as b#456", |
| result: []comment{"# 123", "#456"}, |
| }, |
| } |
| for _, u := range units { |
| t.Run(u.name, func(t *testing.T) { |
| p := NewFileParser() |
| code := []byte(u.code) |
| p.SetCodeAndFile(code, "", "") |
| output, err := p.Parse(context.Background()) |
| assert.NoError(t, err) |
| assert.Equal(t, u.result, output.Comments) |
| }) |
| } |
| } |
| |
| func TestParseMain(t *testing.T) { |
| t.Parallel() |
| units := []struct { |
| name string |
| code string |
| result bool |
| }{ |
| { |
| name: "not has main", |
| code: "a = 1\nb = 2", |
| result: false, |
| }, |
| { |
| name: "has main in function", |
| code: `def foo(): |
| if __name__ == "__main__": |
| a = 3 |
| `, |
| result: false, |
| }, |
| { |
| name: "has main", |
| code: ` |
| import unittest |
| |
| from lib import main |
| |
| |
| class ExampleTest(unittest.TestCase): |
| def test_main(self): |
| self.assertEqual( |
| "", |
| main([["A", 1], ["B", 2]]), |
| ) |
| |
| |
| if __name__ == "__main__": |
| unittest.main() |
| `, |
| result: true, |
| }, |
| } |
| for _, u := range units { |
| t.Run(u.name, func(t *testing.T) { |
| p := NewFileParser() |
| code := []byte(u.code) |
| p.SetCodeAndFile(code, "", "") |
| output, err := p.Parse(context.Background()) |
| assert.NoError(t, err) |
| assert.Equal(t, u.result, output.HasMain) |
| }) |
| } |
| } |
| |
| func TestParseFull(t *testing.T) { |
| p := NewFileParser() |
| code := []byte(`from bar import abc`) |
| p.SetCodeAndFile(code, "foo", "a.py") |
| output, err := p.Parse(context.Background()) |
| assert.NoError(t, err) |
| assert.Equal(t, ParserOutput{ |
| Modules: []module{{Name: "bar.abc", LineNumber: 1, Filepath: "foo/a.py", From: "bar"}}, |
| Comments: nil, |
| HasMain: false, |
| FileName: "a.py", |
| }, *output) |
| } |