Tài liệu Câu hỏi ôn tập môn xây dựng phần mềm: Xây dựng pm
CÂU HỎI ÔN TẬP MÔN XÂY DỰNG PM
Câu 1: Trình bày các khái niệm của xây dựng pm.
Lên kế hoạch xây dựng phát triền dự án tập trung vào code, debug, unit test và
thiết kế cụ thể, 1 phần nhỏ nữa là tích hợp và kiểm chứng tích hợp
Xây dựng pm là 1 phần lớn trong quá trình phát triển pm, kết quả của quá trình
xây dựng pm là 1 pm.
(1 pm bao gồm: bản yêu cầu pm, bản kế hoạch phát triển pm, phân tích thiết kế pm, mã
nguồn pm, phân tích kiểm thử pm, tích hợp pm, hướng dẫn sd, hướng dẫn cài đặt.)
Phần quan trọng nhất của xây dựng pm là mã nguồn, và nó cũng là phần quan
trọng nhất của 1 pm
Xây dựng pm là phần trọng tâm và bắt buộc trong quá trình phát triển pm .
Xây dựng pm như thế nào ?
1. Cách viết code sơ đẳng là viết code thử, nếu sai thì bỏ
2. Cách viết code chương trình được định nghĩa giống như gieo cây, ta viết và thử
từng đoạn nhỏ, rồi chạy. Nhưng đối với đồ án lớn, các đoạn nhỏ không kết nối được, phát
sinh lỗi, thì không hiệu quả .
3. Viết co...
24 trang |
Chia sẻ: Khủng Long | Lượt xem: 972 | Lượt tải: 0
Bạn đang xem trước 20 trang mẫu tài liệu Câu hỏi ôn tập môn xây dựng phần mềm, để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên
Xây dựng pm
CÂU HỎI ÔN TẬP MÔN XÂY DỰNG PM
Câu 1: Trình bày các khái niệm của xây dựng pm.
Lên kế hoạch xây dựng phát triền dự án tập trung vào code, debug, unit test và
thiết kế cụ thể, 1 phần nhỏ nữa là tích hợp và kiểm chứng tích hợp
Xây dựng pm là 1 phần lớn trong quá trình phát triển pm, kết quả của quá trình
xây dựng pm là 1 pm.
(1 pm bao gồm: bản yêu cầu pm, bản kế hoạch phát triển pm, phân tích thiết kế pm, mã
nguồn pm, phân tích kiểm thử pm, tích hợp pm, hướng dẫn sd, hướng dẫn cài đặt.)
Phần quan trọng nhất của xây dựng pm là mã nguồn, và nó cũng là phần quan
trọng nhất của 1 pm
Xây dựng pm là phần trọng tâm và bắt buộc trong quá trình phát triển pm .
Xây dựng pm như thế nào ?
1. Cách viết code sơ đẳng là viết code thử, nếu sai thì bỏ
2. Cách viết code chương trình được định nghĩa giống như gieo cây, ta viết và thử
từng đoạn nhỏ, rồi chạy. Nhưng đối với đồ án lớn, các đoạn nhỏ không kết nối được, phát
sinh lỗi, thì không hiệu quả .
3. Viết code có thể chuẩn hóa dần (viết đoạn nhỏ, đảm bảo tương thích với các
đoạn code trước đó).
Để xây dựng pm tốt, thì phải lên bản thiết kế (giống như xây dựng nhà), đồ án càng
phức tạp, bản thiết kế càng chi tiết. Đó là điều bắt buộc để xây dựng 1 pm tốt.
o Trước khi xdpm cần phải định nghĩa vấn đề cần giải quyết (làm gì, cho ai và cái
gì).
o Thứ 2 phải có yêu cầu.
o Tiếp theo cần phải có bản phân tích thiết kế hệ thống cụ thể, phải có từng thành
phần nào, các lớp liên hệ với nhau ra sao, phải có sơ đồ ghi rõ trước khi xây dựng.
o Sau đó cần phải chọn được ngôn ngữ lập trình.
o Kế tiếp cần phải chọn nền tảng công nghệ, thực ra chỉ là thư viện viết sẵn để
chúng ta tái sử dụng. Vì công nghệ giúp ta tiết kiệm thời gian và công sức khi xây
dựng pm.
o Ta phải cần phải có các chuẩn về coding. Chuẩn về coding ta cần quan tâm :
Xây dựng pm
Định dạng thụt vào của code (indentation), vertical alignment: theo hàng
dọc và có thứ tự.
Khoảng trống, các tab.
Cách đặt tên biến: tên phải luôn luôn có nghĩa và mang tính gợi nhớ, cần
phải viết hoa, viết thường 1 cách hợp lý.
Boolean values in decision structure: trình bày đúng logic
Phép gán
Các vòng lặp, không được quá rắc rối, và nhiều vòng lặp.
Mỗi ngôn ngữ sẽ có 1 loại chuẩn riêng. Mỗi nhóm / công ty sẽ tự đặt ra 1 chuẩn
về coding cho mình. Nếu chúng ta có chuẩn của code, sẽ giúp chúng ta dễ dàng
đọc code của thành viên, ráp code cũng dễ dàng, và giúp làm việc nhóm trôi
chảy.
o Sau khi có chuẩn code, chúng ta phải xác định được công cụ cần thiết để chúng ta
xây dựng pm.
o Cuối cùng chúng ta cần có nơi để lưu trữ mã nguồn, để sử dụng cho tất cả các
thành viên trong nhóm.
Cái quan trọng nhất của xây dựng pm là IDE (Môi trường phát triển tích hợp). 1 IDE
thông thường bao gồm công cụ lập trình để biên tập mã nguồn, trình biên dịch / thông
dịch, công cụ để build, cuối cùng cần có chương trình để debug. Khả năng tra từ điển là
phần quan trọng của IDE, IDE tự động viết mã nguồn khi ta phát sinh 1 giao tác xử lý.
Câu 2: Trình bày các khái niệm và lợi ích của quản lý phiên bản.
Quản lý phiên bản ghi lại tất cả các thay đổi khi người dùng thao tác
- Đối với người làm việc độc lập:
+ Có thể dễ dàng quay trở về phiên bản cũ
+ Dễ dàng kiểm tra lại code của mình trước khi commit lên thùng chứa
+ Đồng bộ nhiều máy tính
- Đối với làm việc tập thể:
+ Dễ dàng tập trung dữ liệu và quản lý cấu hình các tập tin có trong thùng chứa
+ Dễ dàng đồng bộ dữ liệu giữa các máy tính
+ 1 người có thể tự làm phần việc của mình mà ko làm phiền người khác
Xây dựng pm
+ Merge 1 cách tự động
- Lợi ích của phân quyền quản lý phiên bản:
+ Dễ dàng phân nhánh hoặc trộn lẫn
+ Đơn giản hóa việc quản lý phân quyền
+ Ngắt kết nối hoạt động
+ Dễ dàng xem, cập nhật dữ liệu, và so sánh sự khác nhau giữa các phiên bản
+ Có thể tạo patch để làm việc khi không kết nối mạng
+ Tạo nhánh cho dự án dễ dàng hơn từ đó giúp cho các developer dễ dàng làm việc với
nhau và quản lý dự án
- Các khái niệm của Quản lý phiên bản:
+ The Repository: thùng chứa đóng vai trò như 1 kho lưu trữ thông tin dưới hình thức 1
cây hệ thống tập tin, cho phép nhiều client kết nối vào lấy dữ liệu về để đọc và ghi.
Thùng chứa khi mới tạo đc quy định là phiên bản 0
+ Working Copy: Phiên bản làm việc: là bản sao của tập tin dữ liệu mà người dùng làm
việc
.Có 4 trạng thái: (state)
Unchanged, and current
Locally changed, and current
Unchanged, anh out-of-date
Locally changed, and out-of-date
+ Workspaces: Môi trường làm việc của developer tách biệt khỏi Thùng Chứa và cô lập
với những người sử dụng khác
+ Revision: Phiên bản của Thùng Chứa
Revision Numbers: số thứ tự được đánh dấu cho từng phiên bản
Revision Keywords:
Base: Phiên bản vừa đc lấy về, chưa thay đổi
Head: Phiên bản mới nhất của thùng chứa
Committed: Phiên bản của file vừa đc đưa vào thùng chứa
Xây dựng pm
Mixed Revision: Các file trong thùng chứa có phiên bản khác nhau
Revision Dates: Ngày mà phiên bản đc đưa vào thùng chứa
+ Deltas: Sự khác nhau giữa 2 phiên bản
+ CheckOut / Update: thực hiện lấy mã nguồn từ Thùng Chứa để thao tác với file dữ liệu
+ Modify / Edit: Thao tác thay đổi mã nguồn ở môi trường làm việc của developer
(workspace)
+ Check In / Commit: Thực hiện để lưu lại những thay đổi trên dữ liệu và đưa vào Thùng
Chứa
+ Conflict: Là tình trạng mà 2 người dùng cùng commit dữ liệu lên Thùng Chứa cùng 1
thời điểm
+ Lock-Modify-Unlock: Là thao tác dùng để giải quyết tình trạng Conflict. Khi 1 người
dùng checkout file dữ liệu về thì thực hiện thao tác lock Thùng Chứa lại để ko cho phép
người dùng khác check out file dữ liệu đó từ Thùng chứa về. Sau đó thì thực hiện
chỉnh sửa mã nguồn rồi commit lại lên thùng chứa rồi thực hiện Unclock để người dùng
khác có thể check out và sử dụng file dữ liệu đó
+ Copy-Modify-Merge: Là thao tác để kết hợp các sự thay đổi của 2 phiên bản làm việc
(working copy) khác nhau thành 1 phiên bản làm việc mới
+ Branch: Là 1 nhánh của quá trình phát triển, tồn tại độc lập với các nhánh phát triển
khác
+ Merging Branches: Là sự kết hợp các thay đổi của 1 nhánh này vào 1 nhánh khác
+ Tag/Label: Là điểm ảnh của 1 dự án tại 1 thời điểm nào đó. Thường dùng để back up
file khi gặp vấn đề
+ Exclusive Development: Hệ thống cho phép chỉ 1 người dùng tại 1 thời điểm được
phép làm việc trên bất kỳ file dữ liệu nào.
+ Simultaneous Development: Hệ thống cho phép nhiều người cùng làm việc trên các
working copy từ 1 file dữ liệu. Và hệ thống sẽ merge các phiên bản làm việc lại với nhau.
+ Central Repository: Thùng chứa tập trung cho phép mọi người đều có quyền kết nối
và lấy dữ liệu.
Xây dựng pm
Thùng chứa tập trung đơn giản và dễ sử dụng, nhưng mọi thao tác đối với dữ liệu
trên thùng chứa tập trung đều sẽ ảnh hưởng chung đến tất cả những người sử dụng
+ Distributed Repositories: Là Thùng chứa phân bổ, mỗi người dùng sẽ có Thùng chứa
riêng, có thể kết nối với nhau để cập nhật thay đổi. Đối với loại Thùng chứa phân bổ, nếu
dữ liệu trên 1 thùng chứa bị mất thì cũng sẽ ko ảnh hưởng đến các thùng chứa khác. Tuy
nhiên sẽ rất tốn không gian cho loại thùng chứa này.
+Proxied Repositories: Thùng chứa trung gian bao gồm 1 thùng chứa tập trung và nhiều
thùng chứa phân bổ
Thùng chứa trung gian gây tốn tài nguyên và không gian.
+Remote Access To The Repository: Là hình thức truy cập vào thùng chứa bằng Internet
Câu 3: Trình bày các câu lệnh cơ bản để làm việc với Subversion.
Các câu lệnh cơ bản để làm việc với Subversion
-svn:
+ add: Lệnh này sẽ đánh dấu việc thêm 1 file hay thư mục vào hệ thống
Subversion trên phiên bản làm việc.
File hoặc thư mục được thực sự thêm vào Thùng chứa khi ta thực hiện tiếp
lệnh "svn commit"
$ svn add PATH
+ blame: Lệnh này xuất ra nội dung của tập tin mà người dùng muốn xem, cùng
với tên tác giả và số thứ tự của phiên bản được đánh dấu trên từng dòng nội dung của tập
tin. Tên tác giả là username của người dùng đã thay đổi nổi dung của dòng tập tin sau
cùng, và số thứ tự của phiên bản cho biết việc thay đổi nội dung tập tin đã xảy ra trên
những dòng nào
$ svn blame PATH or
$ svn blame URL
+ cat: Giống với lệnh svn blame nhưng nội dung xuất ra theo dạng chuẩn, ko có
đánh dấu số thứ tự phiên bản như blame
$ svn cat PATH or
Xây dựng pm
$ svn cat URL
+ checkout (co): lệnh lấy về một phiên bản tùy chọn từ thùng chứa
+ cleanup:
+ commit (ci): đưa file đã modify lên thùng chứa
+ copy (cp):
+ delete (del, remove, rm):
+ diff (di): Lệnh này xuất ra những điểm khác nhau giữa 2 phiên bản của 1 file dữ
liệu
$ svn diff --revision N:M URL or
$ svn diff URL1@N URL2@M or
$ svn diff --revision N:M URL1 URL2
+ import: Lệnh này được dùng để đưa 1 file chưa được đánh dấu phiên bản lên
Thùng chứa.
Khác với lệnh svn add, lệnh này đc thực thi ngay lập tức mà ko phải chờ
đánh dấu trên phiên bản làm việc rồi sử dụng lệnh svn commit để đưa file lên thùng chứa.
Lệnh này thường được dùng để đưa những dữ liệu đầu tiên vào thùng chứa
+ lock: lệnh khóa 1 file để ko cho ai khác làm việc trên nó
+ log: Lệnh này xuất ra lịch sử thay đổi nội dung dữ liệu của từng phiên bản
+ merge:
+ mkdir: tạo thư mục
+ resolved: xử lý khi bị confict
+ revert: Lệnh này dùng để quay trở về phiên bản
+ status (stat, st): Lệnh này để xuất ra trạng thái hiện tại của tập tin dữ liệu trên
thùng chứa vừa đc lấy về máy của người dùng,
option -v đi kèm để yêu cầu xuất ra đầy đủ các thông tin bao gồm số
thứ tự của phiên bản và tác giả
Xây dựng pm
+ switch (sw):
+ update (up): cập nhật phiên bản mới nhất từ thùng chứa
-svnadmin:
+ create:
-svnlook:
+ info:
+ tree: In ra cây thư mục/tập tin tương ứng của 1 phiên bản thùng chứa nhất định
$ svnlook tree -r REViSION REPOSITORY [PATH]
- Tạo Thùng chứa:
+ Dùng lệnh mkdir để tạo thư mục làm thùng chứa
+ Dùng lệnh svnadmin create [PATH của thư mục vừa tạo làm thùng chứa]
- Tạo trunk trong thùng chứa vừa tạo:
+ Dùng lệnh svn mkdir [URL: <Tên của thư mục làm thùng
chứa>/trunk] -m ""//option này dùng để gửi thông điệp kèm theo
- Tạo branches trong thùng chứa vừa tạo:
+ Dùng lệnh svn mkdir [URL: <Tên của thư mục làm thùng
chứa>/braches] -m ""//option này dùng để gửi thông điệp kèm theo
- Tạo tags trong thùng chứa vừa tạo:
+ Dùng lệnh svn mkdir [URL: <Tên của thư mục làm thùng
chứa>/tags] -m ""//option này dùng để gửi thông điệp kèm theo
- Đưa những source code đầu tiên chưa từng được đánh dấu phiên bản lên Thùng chứa
+ Từ máy của người dùng thứ 1, PATH đường dẫn đến thư mục chứa source code
+ Dùng lệnh svn import [URL của thư mục sẽ nhận source code] -m "<thông
điệp>"
//Lệnh svn import khác svn add ở chỗ là lệnh này được thực thi ngay lập tức mà ko cần
đến phiên bản làm việc (working copy)
Xây dựng pm
//ai ko hiểu 2 lệnh này thì pm Th :D
- Từ máy của người dùng thứ 2 lấy dữ liệu từ thùng chứa về:
+ Từ máy của người dùng thứ 2, PATH đường dẫn đến thư mục sẽ chứa dữ liệu đc
lấy về
+ Dùng lệnh: svn checkout [URL của thư mục chứa source code trong localhost]
+ Hoặc lệnh: svn co [URL của thư mục chứa source code trong localhost]
- Từ máy của người dùng: Commit dữ liệu lên thùng chứa
+ PATH là đường dẫn đến thu mục đang chứa dữ liệu muốn commit
+ Dùng lệnh: svn commit -m "" [PATH/URL/tên file]
+ Hoặc dùng lệnh: svn update //lệnh này ko cần option theo sau vì nó sẽ tự động
tìm những thay đổi mới trên phiên bản làm việc để update với phiên bản cũ
+ Kiểm tra lại dữ liệu đã update/commit lên thùng chứa:
.Dùng lệnh: svn log [URL/PATH/tên file]
.Hoặc dùng lệnh: svn diff --revision BASE:HEAD/PREV:COMMITTED/...
[URL/PATH/tên file]
.Hoặc dùng lệnh: svn diff [tên file] //lệnh này sẽ tự so sánh giữa revision
của file đang có trên thùng chứa với phiên bản làm việc trên máy của người dùng
.Hoặc dùng lệnh: svn blame [PATH/URL]
.Hoặc dùng lệnh: svn cat [PATH/URL]
.Hoặc dùng lệnh: svn status [PATH]
//Mọi người tự coi định nghĩa các lệnh ở trên để so sánh sự khác nhau giữa các lệnh nha
- Thao tác Lock-Modify-Unlock:
+ PATH là đường dẫn đến thư mục chứa file dữ liệu muốn check out từ thùng
chứa về
+ Dùng lệnh: svn co [URL/PATH] --username để check out/update dữ
liệu từ thùng chứa về máy //--username là option có thể thêm nếu cần
Xây dựng pm
+ Dùng lệnh: svn lock [URL/PATH của file muốn lock]
//Khi đó nếu người dùng khác dùng lệnh: svn commit để gửi file mà người dùng trc đã
lock thì sẽ xuất hiện thông báo lỗi
- Thêm / Xóa
+ PATH là đường dẫn đến thư mục chứa file dữ liệu muốn commit lên Thùng
chứa
+ Thêm: dùng lệnh: svn add [URL/PATH/Tên file muốn thêm] //để đánh dấu file
muốn thêm trên phiên bản làm việc của máy người dùng đó
sau đó dùng lệnh: svn commit -m "" để thực thi lệnh thêm đó
+ Xóa: dùng lệnh: svn delete [URL/PATH/Tên file muốn xóa] //để đánh dấu file
muốn xóa trên phiên bản làm việc của máy người dùng đó
sau đó dùng lệnh: svn commit -m "" để thực thi lệnh xóa đó
- Giải quyết tình trạng Conflict: Tình trạng này xảy ra khi 2 người dùng cùng commit dữ
liệu lên Thùng chứa trong cùng 1 thời điểm. Do đó chỉ có 1 người được phép commit lên
và người còn lại sẽ gặp thông báo lỗi conflict.
+ Để giải quyết tình trạng này thì người dùng bị thông báo lỗi conflict đầu tiên
phải dùng lệnh: svn update hoặc svn co để lấy phiên bản mới nhất từ thùng chứa về máy
mình. Sau đó dùng lệnh: svn resolved [PATH/URL/Tên file mà người dùng thứ 2 đã
chỉnh sửa]
sau đó dùng tiếp lệnh: svn commit -m "" để commit file đó
lên Thùng Chứa
- Tạo tag/branch:
+ Tạo thư mục Tag/Branch trong Thùng chứa bằng lệnh: svn mkdir [URL] -m
""
+ Xem cây tập tin và thư mục trong thùng chứa tại phiên bản mới nhất bằng lệnh:
svnlook tree [PATH]
+ Dùng lệnh: svn copy [URL1] [URL2] để chép dữ liệu từ URL1 sang URL2. Với
URL2 là đường dẫn đến thư mục Tag / Branch đã tạo trc đó
- Merge Branch với Trunk:
Xây dựng pm
+ Xem cây thư mục: svnlook tree [PATH]
+ Từ máy của người dùng thứ nhất: update lên phiên bản làm việc mới nhất: svn
update //Chú ý revision
+ Từ máy của người dùng thứ 2: check log: svn log //để kiểm tra revision ở máy
người dùng thứ 2
+ Nếu có sự khác nhau về revision dùng lệnh: svn merge -r1:2 [URL2] //ở option -
r1:2 thì 1 là số revision của người dùng có revision nhỏ hơn và 2 là ngc lại
+ Giải quyết conflict bằng lệnh: svn resolved [Tên file bị conflict]
+ Commit lên thùng chứa: svn commit -m ""
Câu 4: Trình bày các tính năng và thao tác cơ bản của Visual SourceSafe.
Các tính năng:
- Resource Management: Quản lý mã nguồn tại thùng chứa hoặc cơ sở dữ liệu
- Workspaces: Cung cấp ko gian làm việc riêng cho các thành viên
- Cooperation Support: Quản lý sự ảnh hưởng lẫn nhau của các thành viên
- Build and release Management: Quản lý việc build và release sản phẩm để đảm
bảo cấu hình của sản phẩm đc đánh dấu mỗi khi release để tiện cho việc bảo trì và
sử dụng lại
- Parallel development: Có khả năng phát triển song song nhiều phiên bản của 1
project
- Resource Versioning: Bảo trì những version khác nhau của Resource
- History Management: Quản lý, đánh dấu những phiên bản trong dự án
Các thao tác cơ bản:
- Tạo 1 CSDL mới
o Trong cửa sổ của VSS Administrator -> New Database -> chọn nơi chứa
CSDL và đặt tên cho CSDL đó -> chọn chế độ quản lý: Lock-Modify-
Unlock/ Copy-Modify-Merge
- Thêm 1 User vào hệ thống
o Trong cửa sổ của VSS Administrator -> Menu User -> Add User
- Quản lý dự án
Xây dựng pm
o Dùng Username và password Log on vào hệ thống -> Ở màn hình VSS
Explorer -> right click vào folder muốn lưu trữ Project -> chọn Set working
folder -> Lấy dữ liệu về từ thùng chứa -> Get
- Kết hợp với Visual Studio.Net
o Trong cửa sổ Solution Explorer của VS -> right click vào Project chọn Add
Solution to Source Control -> right click vào file muốn check out/check in -
> chọn Check out/Check in
o Trong cửa sổ Solution Explorer của VS -> right click vào file muốn view
History -> chọn View History để xem các version
Câu 5: Trình bày các khái niệm của NAnt. Nêu cách build dự án bằng NAnt.
Câu 6: Trình bày các thành phần của NAnt Build Script.
Câu 7: Trình bày các tác vụ cơ bản của NAnt.
Câu 8: Trình bày các khái niệm của MSBuild. Nêu cách build dự án bằng
MSBuild.
1) Khái niệm: MSBuild là cơ chế biên dịch không có giao diện người dùng, mọi tính
năng đều được dấu bên trong.
Công việc biên dịch phụ thuộc vào tệp dựa theo chuẩn XML với phần mở rộng
.proj
2) Cách build dự án:
//Bạn nào biết viết thêm giùm mình
Câu 9: Trình bày các thành phần của MSBuild Build Script.
1) Properties: là khái niệm gồm 1 cặp khóa và giá trị xen giữa
//Còn nhiều bạn nào viết thêm giùm mình
2) Items: là thẻ định ra các phần kèm theo khi build
Xây dựng pm
a. Thẻ ItemGroup: nhóm nhiều thẻ Item lại với nhau
b. Thành phần của Item:
Type: xác định kiểu của tập tin
Include: dưa ra đường dẫn tập tin
Exclude: liệt kê những gì ko muốn kèm theo khi build
Condition: các yêu cầu đưa ra khi build
3) Targets: được xe như là 1 thùng chứa các Task sẽ được thực thi
a. Thuộc tính Name: thuộc tính bắt buộc. Thuộc tính này sẽ cần đến khi ta gọi
1 Target thực thi
b. Một số thuộc tính khác: Inputs, Outputs, DependsOnTargets, Condition
c. Predefined Target: là những target đã được định nghĩa sẵn và chúng ta chỉ
sử dụng chúng
d. DefaultTargets: target được chỉ định sẽ được build đầu tiên. Nếu ko có thì
target đầu tiên sẽ được thực thi
4) Tasks: là 1 đoạn code, 1 lớp để thực thi 1 hành động nào đó khi build chương
trình, nó kế thừa các phương thức do MSBuild cung cấp
5) MSBuild Logging: nhật kí ghi lại trong quá trình Build
MSBuild Logger: chương trình cho phép ghi nhận lại các sự kiện, các thông điệp,
các lỗi xảy ra hay các cảnh báo xảy ra trong quá trình build
6) MSBuild Batching: MSBuild có khả năng chia tập hợp các items thành các mục
khác nhau dựa trên các item metadata – gọi là các batch, và thực hiện các task hay
target cho mỗi batch
a. Task batching: cho phép bạn làm đơn giản file project của bạn bằng cách
chia tập hợp các item thành các batches và truyền mỗi batch đó vào task
một các riêng biệt.
b. Target batching: Msbuild kiểm tra các input và output của target trước khi
thực hiện target đó
Câu 10: Trình bày các tác vụ cơ bản của MSBuild (cùng các tham số).
1) Copy: thực hiện copy các file vào 1 thư mục
SourceFiles: các file cần copy (nguồn)
DestinationFolder: thư mục đích
FilesCopied: tập chứa các file copy thành công
Xây dựng pm
2) Csc: compile 1 project với source C#.
Source: file source của C#
TargetType: định dang file output là 1 module, 1 library hay 1 file winexe
3) Vbc: tương tự Csc dùng compile các project viết bằng VB
4) Delete: xóa file
Files: các file cần xóa
DeletedFiles: các file đã xóa thành công
5) Exec: chạy 1 chương trình ứng dụng từ bên ngoài.
Command: cho biết command cần thực hiện hoặc đường dẫn đến file thực
thi.
WorkingDirectory: đường dẫn thư mục nơi command thự hiện.
6) MakeDir: tạo thư mục
Directories: tên thư mục cần tạo và đường dẫn
CreatedDirectories: gồm các thư mục đã tạo thành công
7) RemoveDir: xóa thư mục
Directories: thư mục cần xóa
RemovedDirectories: tên các thư mục đã xóa thành công
8) Message: đưa ra console chuỗi thông báo
9) MSBuild: build 1 project MSBuild khác.
Project: project MSBuild cần build
Properties: các properties tương ứng
Targets: các target sẽ build trong project ta cần build
Câu 11: Trình bày các quy tắc tối ưu hóa mã nguồn.
a. Biến
- Sử dụng biến đúng mục đích cần dùng .Thời gian ràng buộc sớm hơn, thì độ
linh hoạt thấp và độ phức tạp giảm.
b. Tránh lồng phân cấp 3-4 mức .
Xây dựng pm
c. Làm mọi thứ càng đơn giản càng tốt .
d. Độ phức tạp Complexity: Code càng đơn giản càng tốt
+Bắt đầu bằng 1. Với mỗi câu lệnh if, while, repeat, for, and, or thì cộng 1
+Độ phức tạp từ 0-5: chấp nhận dc
từ 6-10: rút gọn code
từ 10: tách hàm
e. Biểu thức Boolean: Phải trình bày theo logic
Câu 12: Trình bày các khái niệm, lợi ích và hạn chế của tích hợp liên tục.
1) Khái niêm :
- Lấy mã nguồn.
- Phát triển chức năng .
- Tạo 1 build trên máy mình .
- Chạy unit test auto .
- Đưa mả nguồn lên thùng chứa .
- Build tích hợp.
2) Build khi có bất kì sự thay đổi nào :
- Viết script tự động .
- Thực hiện từng phiên bản build 1.
- Tách biệt build script khỏi IDE. (vs, netbean)
- Tập trung hóa các tài nguyên .
- Tạo cấu trúc thư mục hợp lý.
- Build có lỗi phải dừng ngay (build fail)
- Build phải chạy trong bất kì môi trường
- Sử dụng máy build tích hợp riêng.
- Sử dụng server tích hợp lien tục riêng .
- Chạy build script bằng tay
- Chạy build nhanh
- Build theo từng công đoạn.
3) Lợi ích
- Giảm rủi ro.
- Dễ dàng phát hiện và xóa lỗi .
- Những build luôn được cập nhật .
- Tiến trình được quản lí.
4) Rủi ro.
- Khó khăn trong việc quản lý nhiều threads cùng chạy trên 1 ứng dụng.
Xây dựng pm
Câu 13: Trình bày các quy tắc của tích hợp liên tục.
- Build pm mổi khi thay đổi.
- Tích hợp csdl lien tục.
- Test liên tục.
- Thanh tra lien tục ( continous inspection )
+ Giảm độ phức tạp của code
+ Xem xét phần design
+ Giữ vững code chuẩn.
+ Loại bỏ những code lặp lại.
+ Xem xét mức độ truy cập của code
- Triển khai lien tục (continous deployment)
+ Release làm việc bất kì thời điểm, bất kì môi trường nào
+ Đánh dấu phiên bản.
+ Sản phẩm được triển khai ở môi trường sạch.
+ Đánh dấu toàn bộ tài nguyên.
+ Chạy tất cả test.
+ Tao ra những feedback.
+ Tạo ra khả năng rollback.
Câu 14: Trình bày các khái niệm của Unit Test. Nêu cách viết Unit Test.
- Tổng quan về unittest:
Sau khi viết code xong, lập trình viên phải thực hiện việc kiểm tra xem code của mình
viết có đúng không. Công việc này gọi là unit test. Unit test theo cách hiểu đơn giản đó là
kiểm tra từng bộ phận rất nhỏ trong source code của chương trình, từ những bộ phận nhỏ
này, ta lại kiểm tra những unit lớn hơn có sử dụng những unit nhỏ đã được kiểm tra đó.
Một số thông tin về unit test:
Unit test được thực hiện bởi lập trình viên
Bộ phận QA sẽ thực hiện test nhưng không phải là unit test
Mọi lập trình viên đều phải thực hiện và báo cáo kết quả unit test trước ,trong hoặc
sau khi develop
Unit test có thể thực hiện tự động, sử dụng các đoạn code gọi là test script,quá
trình này gọi là automated unit testing
Xây dựng pm
Trong automated unit testing, ta sẽ viết các hàm kiểm tra, gọi các hàm cần kiểm
tra. Nếu kết quả ra như mong đợi thì kết quả là pass, ngược lại là failed. Sau khi
thực thi tất cả các hàm kiểm tra, ta sẽ có được 1 bảng tóm tắt kết quả: bao nhiêu
Pass, bao nhiêu Failed và các lý do
Automated unit test scrip có thể được lưu và chạy lại 1 cách dễ dàng khi mã nguồn
thay đổi
Nunit là 1 framework giúp chúng ta thực hiện unit testing trên .NET. Ngoài Nunit
chúng ta còn có Junit cho java, cppunit cho cpp, phpunit cho php..
- Cách viết Unit test: ta lấy Nunit làm ví dụ
Cấu hình :
Tạo project Unittest.
Sau đó vào File –Open – Project solution – Chọn Project đã chọn để test (nhớ
click chọn vào Add solution).
Trong project test ,click phải vào chữ Reference - add reference,sau đó tìm
file nunit.framework trong thẻ .NET.
Thêm thư viện: using NUnit.Framework vào class của project unittest.
Add dependency cho project unittest (check vào project được chọn để test)
Cuối cùng tiến hành add reference file dll của project chọn để test vào project
unittest.
Viết test:
Thêm attribute [TestFixture] vào trước class định nghĩa ,attribute này báo cho
Nunit biết đây là lớp unit test
Khởi tạo đối tượng dùng để test .Việc khởi tạo sẽ nằm trong hàm Setup () có
thuộc tính là [TestFixtureSetUp] được đặt phía trước hàm SetUp().
Sau khi test xong,các đối tượng phải được hủy ,công việc hủy nằm trong hàm
TearDown() ,và nằm dưới thuộc tính [TestFixtureTearDown]
Ta bắt đầu viết test với thuộc tính [Test] ở đầu dòng
Xây dựng pm
Các hàm của Nunit hỗ trợ việc kiểm tra kết quả như :
AreEqual,Less/Greater,GreaterOrEqual /LessorEqual ,IsNull/IsNotNull nếu
result và kết quả mong đợi mà chúng ta tự gán đúng điều kiện yêu cầu, thì hàm
test này sẽ Pass ngược lại sai sẽ Failed
Thuộc tính ExpectedException (typeof(DivideByZeroException)) dùng để báo
biết phương thức Divide có thể trả về 1 exception là
DivideByZeroException.Khi exception đó xảy ra ,test case vẫn pass.
Thực thi test : vào Nunit – File – Open Project chọn file dll nằm trong thư mục
debug của project.Nhấn Open.Sau khi chạy xong, sẽ cho ta biết test nào
pass,test nào lỗi và lý do của nó .
Câu 15: Trình bày các loại Mock Objects. Nêu phương pháp sử dụng chúng.
Mock object (MO) là một đối tượng ảo mô phỏng các tính chất và hành vi giống hệt
như đối tượng thực được truyền vào bên trong khối mã đang vận hành nhằm kiểm tra tính
đúng đắn của các hoạt động bên trong.
- Phân loại mock:
Static mock: được viết bằng tay hay phát sinh tự động ở một bước nào đó trong
chương trình
Synamic mock: phụ thuộc vào proxy interface
- Cách sử dụng mock object: ta ví dụ sử dụng trong Nunit
Để sử dụng được mock, ta phải add thư viện vào, trong Nunit thư việc sẽ là
nunit.mocks.dll và using package Nunit.Mock.Nunit cung cấp lớp
DynamicMock dùng để tạo mock. Để sử dụng mock, ta có 2 bước sau :
o Bước 1: Bảo cho mock biết đối tượng giả lập sẽ có những hàm gì và kết
quả của hàm đó sẽ là gì .Ví dụ :
personRepositoryMock.ExpecAndReturn (“GetPeople”,peopleList)
Ở đây ta nói với mock rằng đối tượng giả lặp sẽ có hàm tên là
GetPeople và kết quả trả về của hàm này là peopleLisst
o Bước 2: Sử dụng mock: Sauk hi đã thiết lập mock xong, ta có thể lấy
đối tượng giả lập bằng câu lệnh :
(IPersonReposotory) personRepositoryMock.MockInstance
Xây dựng pm
Câu lệnh trên sẽ cho ta 1 đối tượng giả lập đối tượng
IPersonReposotory. Khi đối tượng service gọi hàm GetPeople từ đối
tượng này, đều sẽ cho kết quả là peopleList.
Câu 16: Trình bày các khái niệm và quá trình làm việc của WiX.
Wix là 1 công cụ cho phép tạo ra file cài đặt window từ những file đặt tả xml.
Source code của wix viết theo dạng chuẩn của xml.
Ta có thể sử dụng Wix trên cả 2 môi trường: Command line và Visual Studio IDE.
Wix dùng công cụ candle.exe xử lý trước tập tin đầu vào đuôi .wxs, chuyển chúng
sang dạng chuẩn của tài liệu xml dựa trên giảng đồ Wix (wix.xsd). Sau đó mỗi tập tin
nguồn .wxs biên dịch thành tập tin .wixobj. Tập tin wixobj tổng hợp các nguồn cung cấp
tham số đầu vào, các dữ liệu thô để cung cấp cho công cụ Light tìm kiếm và đóng gói.
Tiếp theo công cụ Light, sẽ xử lý 1 hoặc nhiều tập tin .wixobj. Light lấy dữ liệu từ các tập
tin được mô tả và tạo bộ cài đặt MSI hoặc MSM. Trình liên kết bắt đầu bằng việc tìm
kiếm các tập tin .wixobj được cung cấp trên dòng lệnh để tìm entry section. Nếu có nhiều
hơn 1 entry session được tìm thấy, Light sẽ thất bại và trả về lỗi (không thể tại ra 2 file
cài đặt từ 1 trình liên kết). Sau khi đã tìm thấy tất cả các tham số đầu vào, Wix sẽ hoàn tất
việc đóng gói bộ cài đặt cho pm.
Câu 17: Trình bày các thành phần của WXS.
Các thẻ quan trọng trong file Wxs:
- : thẻ gốc
- : chứa thông tin chính sản phần cần build để tạo file msi
: chứa thông tin chính sản phẩm cần build để tạo file msm
: chứa thông tin chính sản phẩm cần build để tạo ra file msp
- :chứa thông tin các đơn vị cần thêm vào file cài đặt
Ta xét đơn cử thẻ Product :
- Các thuộc tính chính của thẻ :
ID: mã của sản phẩm
Xây dựng pm
Codepage: mã số nguyên của trang code hoặc tên của trang web
Langue: mã ngôn ngữ
Manufacture: tên nhà sản xuất
Name: tên của sản phẩm
UpgradeCode: mã nâng cấp của sản phẩm
Version: Phiên bản của sản phẩm
- Các thẻ con chính của thẻ
: Chứa thông tin về gói cài đặt
: mô tả nơi chứa file cài đặt
: Thông tin thư mục chứa file cài đặt,trong directory còn có thẻ
, trong có thẻ : chỉ là con của component
: them chiếu đến thẻ khác
: chứa thông tin đơn vị cài đặt nhỏ nhất
: chứa thông tin icon
Câu 18: Trình bày và cho ví dụ minh họa về Buffer overflow, SQL Injection và
cách phòng chống.
a. Buffer overflow: một khối lượng dữ liệu được gửi cho ứng dụng vượt quá lượng
dữ liệu được cấp phát khiến cho ứng dụng không thực thi được câu lệnh dự định
kế tiếp mà thay vào đó phải thực thi một đoạn mã bất kì do hacker đưa vào hệ
thống.
Hacker làm tràn bộ đệm sao cho con trỏ lệnh sẽ trỏ đến 1 đoạn mã tạo ra 1 giao
diếp dòng lệnh(commandline ,ví dụ như /bin/sln) Sau khi chương trình bị tràn bộ
đệm ,nó sẽ tìm đến địa chỉ của đoạn mã trên để thực thi tiếp .Nếu chương trình
được chạy dưới quyền của người quản trị ,hacker đã có được 1 giao tiếp dòng
lệnh với quyền tương đương và có thể điều khiển toàn bộ hệ thống
+ Ví dụ :
Xây dựng pm
Ta có đoạn code như sau :
char buffer[256]
strcpy (buffer,ch)
Buffer chỉ được cấp phát 256 byte nhưng ở hàm func nếu buffer nhận 257 kí tự từ
ch thì sẽ xảy ra lỗi tràn bộ đệm
+ Cách phòng chống :
Người thiết kế pm cần phải kiểm tra kĩ kích thước dữ liệu trước khi sử dụng ,.
Sử dụng các vùng nhớ cấp phát động thay cho tĩnh khi lưu trử dữ liệu,sử dụng các kĩ
thuật chống tràn trên stack như stack mashing protection ,data execute prevention.Sử
dụng kĩ thuật kiểm tra sâu các gói tin.
Tránh dùng các hàm không cung cấp kiểm tra giới hạn trong ngôn ngữ C ,thay vào đó là
các hàm tương đương :fgets,strncpy,strncat
b. SQL Injection
Là kĩ thuật cho phép khai thác các lỗ hổng bảo mật tồn tại trên cơ sở dữ liệu của 1 ứng
dụng và thi hành các câu lệnh SQL bất hợp pháp.Từ đó có thể toàn quyền trển cơ sở
dữ liệu của ứng dụng ,thậm chí là server mà ứng dụng đó đang chạy.
+ Ví dụ :
Lúc đăng nhập : hacker điền vào user name và password là : „OR‟a=a‟,câu lệnh
select sẽ như sau:
Select from user where username = „‟ OR‟a=a‟ and password = „‟ OR „a=a‟ ->
đăng nhập hợp lệ
+ Phòng chống: Kiểm soát chặt chẽ dữ liệu nhập :xây dựng hàm để thay thế dấu nháy
đơn thành nháy kép.Tránh dùng đến quyền quản trị hệ thống như „sa‟ hay „dbo‟.Sử dụng
tham số liên tục : @user,@password trong code ,sau đó người dùng truyền 1 chuỗi kí tự
tùy ý vào tham số ấy.
Câu 19: Trình bày và cho ví dụ minh họa về CSRF, Session fixation, Session
poisoning và cách phòng chống.
a. CSRF:
Xây dựng pm
- CSRF là gì ?
Là kĩ thuật tấn công bằng cách sử dụng quyền chứng thực của người sử dụng đối với 1
website khác.Các ứng dụng web hoạt động theo cơ chế nhận các câu lệnh http từ người
sử dụng,sau đó thực thi các câu lệnh này. CSRF sẽ lừa trình duyệt của người dùng gửi đi
các câu lệnh http đến các ứng dụng web.Trong trường hợp phiên làm việc của người
dùng chưa hết hiệu lực thì các câu lệnh trên sẽ dc thực hiện với quyền chứng thực của
người sử dụng.
CSRF còn dc gọi là "session riding", "XSRF".
- VD về CSRF: (tức là thông qua 1 hình ảnh hay 1 cách nào đó ,nhử người dùng
click vào 1 link ẩn ,link đó truyền tác vụ đã được chứng thực bởi nạn nhân xử lý
tài khoản trang web mà hacker muốn hack)
Bob duyệt qua 1 diễn đàn yêu thích của mình như thường lệ.Một người dùng
khác,Malory ,đăng tải 1 thông điệp lên diễn đàn .Giả sử rằng Malory có ý đồ k tốt và anh
ta muốn lấy tiền từ những người có tài khoản tại ngân hàng như Bob.Malory sẽ tạo 1
thông báo,trong đó có chèn 1 đoạn code có 1 hình ảnh kích thước là 0x0 pixel. Giả sử
Bob vừa mới truy cập vào tài khoản ngân hàng của mình và chưa thực hiện logout để kết
thúc.Trình duyệt của Bob sẽ gửi câu lệnh http get đến địa chỉ lưu trong thẻ "<img" trong
đoạn mã độc và nó sẽ được thực hiện bằng quyền chứng thực của Bob. Ví dụ: hacker có
thể sử dụng 1 URL để xóa đi một dự án quan trọng nào đó mà Bob đang làm,hoặc có thể
chuyển tất cả tiền trong tài khoản ngân hàng của Bob vào tài khoản của hacker.
- Cách phòng chống :
Hiện tại chưa thực sự có 1 cách phòng chống nào triệt để ,sau đây là 1 vài kĩ thuật sử
dụng :
+ Hạn chế thời gian hiệu lực của session (giống như trang moodle môn học )
+ Dùng phương thức get/post hợp lý : ta dùng phương thức get để truy vấn dữ liệu ,còn
những thao tác làm thay đổi hệ thống thì dùng phương thức post hoặc put.
+ Sử dụng capcha hoặc các thông báo xác nhận : Sử dụng capcha để phân biệt đối tượng
đang thao tác với hệ thống là người hay là tự động,nhất là trong các thao tác như đăng
nhập ,đăng xuất ,thanh toán và chuyển khoản .Tuy nhiên điểm yếu là capcha sẽ gây sự
khó chịu khi sử dụng cho người dùng.Các thông báo xác nhận cũng được sử dụng ,ví dụ
như hiển thị một thông báo : “Bạn có thực sự muốn xóa không?”.
Xây dựng pm
+ Sử dụng token : Tạo 1 token duy nhất cho mỗi form(thường sẽ có đối số là session),khi
nhận lệnh http post về ,hệ thống sẽ thực hiện so khớp để xem có nên thực hiện câu lệnh
hay không .
+ Sử dụng cookie riêng biệt cho phần quản trị
+ Kiểm tra referrer : Kiểm tra câu lệnh http gửi đến xuất phát tự đâu ,và chỉ thực hiện các
lệnh http được gửi đến từ các trang đã được chứng thưck
+ Kiểm tra IP : Một số hệ thống chỉ cho truy cập từ những IP đã được thiết lập sẵn
b. Session Fixation:
Là kỹ thuật tấn công cho phép hacker mạo danh người dùng hợp lệ bằng cách gửi 1
session ID hợp lệ đến người dùng ,sau khi người dùng đăng nhập vào hệ thống thành
công ,haker sẽ dùng lại session ID đó và nghiễm nhiên trở thành người dùng hợp lệ.
Ví dụ :
Đối với 1 hệ thống quản lý session hướng tự do (chấp nhận 1 session ID bất kì)
hacker chỉ cần thiết lập 1 session ID bất kì ,nhớ và sau đó sử dụng lại nó ,đối với hệ
thống quản lý session ID hướng giới hạn,hacker đăng kí 1 session ID với ứng
dụng.Sau đó ,hacker gửi ID có được đến người dùng ,tùy thuộc vào ứng dụng mà có
thể qua URL :trên tham số URL, trong biến ẩn form ,hoặc trong cookie.Sau khi nạn
nhân đăng nhập vào hệ thống qua session ID được chỉ định sẵn ,và chưa thoát khỏi
ứng dụng ,hacker lúc này bắt đầu dùng session ID đó để bước vào phiên làm việc của
nạn nhân,và điều khiển mọi tài khoản liên quan đến nạn nhân.
+ Tấn công trên URL:
Hacker mở dịch vụ trực tuyến của ngân hàng thông qua địa chỉ :
online.worldbank.com .Nhận được 1 session ID từ trình chủ đển xác định phiên làm
việc của hacker .Ví dụ session ID có giá trị là 1234.Sau đó hacker sẽ tìm cách gửi 1
liên kết đến 1 người dùng nào đó có tài khoản trong ngân hàng này .Những liên kết
đó thường là dẫn đến trang đăng nhập vào tài khoản trong ngân hàng ví dụ như liên
kết là : >sessionid=1234 ,để lừa người dùng
làm việc trong phiên làm việc của hacker khi người dùng nhận được liên kết này
.Người dùng bị mắc lừa và mở ứng dụng web bằng liên kết của hacker .Do đã có
session ID (của hacker)nên chu trình sẽ không tạo 1 session ID mới .Người dùng vẫn
tiếp tục đăng nhập với thông tin của mình để quản lý tài khoản .Khi đó hacker sẽ vào
tài khoản của người dùng mà không cần phải đăng nhập vì có cùng phiên làm việc .
Xây dựng pm
+ Tấn công trong biến ẩn form:
Tương tự như ỦL,biến ẩn form nghĩa là sau khi hacker xem mã HTML của trang
web,nhận thấy session ID được đặt trong biến ẩn form ,hacker sẽ gửi 1 session ID
cũng trên URL đến người dùng hoặc 1 trang web giống trang đích nhưng với biến ẩn
form mang giá trị ấn định sẵn .
+ Tấn công trong cookie :
Bằng việc lợi dụng cookie ,hacker có 3 cách để đưa 1 session ID đến trình duyệt của
nạn nhận :
Sử dụng ngôn ngữ kịch bản (javascrip ,vbscrip) để thiết lập 1 cookie trong
trình duyệt của nạn nhân bằng cách thiết lập giá trị “document .cookie=
“sessionid=1234;domain = .worldbank.com” ”.Bên cạnh đó hacker còn có thể
thiết lập thời gian sống cho cookie ,domain cookie.
Dùng thẻ với thuộc tính set-cookie
Thiết lập cookie dùng thuộc tính set-cookie trong header HTTPresponse thông
qua kĩ thuật tấn công DNS server
+ Cách phòng chống :
Chống việc đăng nhập với 1 session ID có sẵn bằng cách hủy bỏ session ID
được cung cấp bởi trình duyệt của người dùng khi đăng nhập và luôn tạo 1
session ID mới khi người dùng đăng nhập thành công .
Chống những hacker bên ngoài hệ thống :tạo ứng dụng trên hệ thống theo
hướng giới hạn (chỉ tạo session ID mới cho người dùng sau khi họ thành công
)
Giới hạn phạm vi ứng dụng của sessionID : Kết hợp session OD với địa chỉ của
trình duyệt ,kết hợp session ID với thông tin chứng thực được mã hóa ssl,xóa
bỏ session khi người dùng thoát khỏi hệ thống hay hết hiệu lực,thiết lập thời
gian hết hiệu lực của session tránh trường hợp hacker có thể duy trình session
và sử dụng nó lâu dài .
c. Session poisoning :
Xây dựng pm
Session poisoning là việc lợi dụng những lỗ hổng của việc quản lý những session trên các
ứng dụng máy chủ để coppy (ăn cắp) những session của người dùng bằng những đoạn
script .
+ Ví dụ
Hacker biết người dùng đang sử dụng 1 ứng dụng web có lỗ hổng ,người dùng nhận
được 1 liên kết thông qua email hay chính trang web đó với những nội dung hấp dẫn như
“click là trúng thưởng” hoặc “chúc mừng bạn đã là người đoạt giải..” Trong liên kết đó
sẽ có 1 đoạn script chạy tự động chỉ chờ người dùng click vào là sẽ đánh cắp session của
người dùng ngay rồi gửi cho hacker .
+ Cách phòng chống :
Tạo ra danh sách những thẻ HTML được phép sử dụng.
Xóa bỏ thẻ script hay lọc ra bất kì 1 đoạn mã javascript /java/vbscript
Lọc dấu nháy đơn ,nháy kép (vì có dấu nháy đơn ,nháy kép có thể chèn đoạn script trong
URL) ,và kí tự null(vì khả năng thêm 1 đoạn mã bất kì sau kí tự null khiến cho ứng dụng
dù đã lọc bỏ thẻ script vấn không nhận ra ,do ứng dụng nghĩ rằng chuỗi đã kết thúc từ kí
tự null này.)
Các file đính kèm theo tài liệu này:
- tailieu.pdf